Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "utils.h"
5#include "qmlutils.h"
6#include "qtmoduleinfo.h"
7#include "qtplugininfo.h"
8
9#include <QtCore/QCommandLineOption>
10#include <QtCore/QCommandLineParser>
11#include <QtCore/QDir>
12#include <QtCore/QFileInfo>
13#include <QtCore/QCoreApplication>
14#include <QtCore/QJsonDocument>
15#include <QtCore/QJsonObject>
16#include <QtCore/QJsonArray>
17#include <QtCore/QList>
18#include <QtCore/QOperatingSystemVersion>
19#include <QtCore/QSet>
20#include <QtCore/QSharedPointer>
21#include <QtCore/QXmlStreamWriter>
22#include <QtNetwork/QSslCertificate>
23
24#ifdef Q_OS_WIN
25#include <QtCore/qt_windows.h>
26#else
27#define IMAGE_FILE_MACHINE_ARM64 0xaa64
28#endif
29
30#include <QtCore/private/qconfig_p.h>
31
32#include <algorithm>
33#include <array>
34#include <cstdio>
35#include <iostream>
36#include <iterator>
37#include <unordered_map>
38
39QT_BEGIN_NAMESPACE
40
41using namespace Qt::StringLiterals;
42
44
45#define DECLARE_KNOWN_MODULE(name)
46 static size_t Qt##name ## ModuleId = QtModule::InvalidId
47
51DECLARE_KNOWN_MODULE(DesignerComponents);
56DECLARE_KNOWN_MODULE(WebEngineCore);
58
59#define DEFINE_KNOWN_MODULE(name)
60 m[QLatin1String("Qt6" #name)] = &Qt##name ## ModuleId
61
63{
64 std::unordered_map<QString, size_t *> m;
65 DEFINE_KNOWN_MODULE(3DQuick);
67 DEFINE_KNOWN_MODULE(Designer);
68 DEFINE_KNOWN_MODULE(DesignerComponents);
71 DEFINE_KNOWN_MODULE(QmlTooling);
73 DEFINE_KNOWN_MODULE(WebEngineCore);
74 DEFINE_KNOWN_MODULE(Widgets);
75 for (size_t i = 0; i < qtModuleEntries.size(); ++i) {
76 const QtModule &module = qtModuleEntries.moduleById(i);
77 auto it = m.find(module.name);
78 if (it == m.end())
79 continue;
80 *(it->second) = i;
81 }
82}
83
84#undef DECLARE_KNOWN_MODULE
85#undef DEFINE_KNOWN_MODULE
86
87static const char webEngineProcessC[] = "QtWebEngineProcess";
88
89static inline QString webProcessBinary(const char *binaryName, Platform p)
90{
91 const QString webProcess = QLatin1StringView(binaryName);
92 return (p & WindowsBased) ? webProcess + QStringLiteral(".exe") : webProcess;
93}
94
95static QString moduleNameToOptionName(const QString &moduleName, bool internal)
96{
97 QString result = moduleName
98 .mid(3) // strip the "Qt6" prefix
99 .toLower();
100 if (result == u"help"_s)
101 result.prepend("qt"_L1);
102 if (internal)
103 result.append("Internal"_L1);
104 return result;
105}
106
107static QByteArray formatQtModules(const ModuleBitset &mask, bool option = false)
108{
109 QByteArray result;
110 for (const auto &qtModule : qtModuleEntries) {
111 if (mask.test(qtModule.id)) {
112 if (!result.isEmpty())
113 result.append(' ');
114 result.append(option
115 ? moduleNameToOptionName(qtModule.name, qtModule.internal).toUtf8()
116 : qtModule.name.toUtf8());
117 }
118 }
119 return result;
120}
121
123{
124 QString result(u'\n');
125 for (const auto &pair : pluginInfo.typeMap()) {
126 result += pair.first;
127 result += u": \n";
128 for (const QString &plugin : pair.second) {
129 result += u" ";
130 result += plugin;
131 result += u'\n';
132 }
133 }
134 return result;
135}
136
137static Platform platformFromMkSpec(const QString &xSpec)
138{
139 if (xSpec.startsWith("win32-"_L1)) {
140 if (xSpec.contains("clang-g++"_L1))
142 if (xSpec.contains("clang-msvc++"_L1))
144 if (xSpec.contains("arm"_L1))
146 if (xSpec.contains("g++"_L1))
147 return WindowsDesktopMinGW;
148
149 return WindowsDesktopMsvc;
150 }
151 return UnknownPlatform;
152}
153
154// Helpers for exclusive options, "-foo", "--no-foo"
160
161static ExlusiveOptionValue parseExclusiveOptions(const QCommandLineParser *parser,
162 const QCommandLineOption &enableOption,
163 const QCommandLineOption &disableOption)
164{
165 const bool enabled = parser->isSet(enableOption);
166 const bool disabled = parser->isSet(disableOption);
167 if (enabled) {
168 if (disabled) {
169 std::wcerr << "Warning: both -" << enableOption.names().first()
170 << " and -" << disableOption.names().first() << " were specified, defaulting to -"
171 << enableOption.names().first() << ".\n";
172 }
173 return OptionEnabled;
174 }
175 return disabled ? OptionDisabled : OptionAuto;
176}
177
178struct Options {
184
185 bool plugins = true;
186 bool libraries = true;
187 bool quickImports = true;
188 bool translations = true;
189 bool systemD3dCompiler = true;
190 bool systemDxc = true;
191 bool compilerRunTime = false;
193 bool ffmpeg = true;
199 unsigned updateFileFlags = 0;
200 QStringList qmlDirectories; // Project's QML files.
201 QStringList qmlImportPaths; // Custom QML module locations.
202 int qmlImportTimeout = 30000;
205 QString translationsDirectory; // Translations target directory
212 JsonOutput *json = nullptr;
215 bool deployPdb = false;
216 bool dryRun = false;
217 bool patchQt = true;
220 bool forceOpenSslPlugin = false;
221 bool createAppx = false;
223};
224
225// Return binary to be deployed from folder, ignore pre-existing web engine process.
226static inline QString findBinary(const QString &directory, Platform platform)
227{
228 const QStringList nameFilters = (platform & WindowsBased) ?
229 QStringList(QStringLiteral("*.exe")) : QStringList();
230 const QFileInfoList &binaries =
231 QDir(QDir::cleanPath(directory)).entryInfoList(nameFilters, QDir::Files | QDir::Executable);
232 for (const QFileInfo &binaryFi : binaries) {
233 const QString binary = binaryFi.fileName();
234 if (!binary.contains(QLatin1StringView(webEngineProcessC), Qt::CaseInsensitive)) {
235 return binaryFi.absoluteFilePath();
236 }
237 }
238 return QString();
239}
240
241static QString msgFileDoesNotExist(const QString & file)
242{
243 return u'"' + QDir::toNativeSeparators(file) + "\" does not exist."_L1;
244}
245
251
253{
254 return {
255 u"qmake"_s,
256 u"Use specified qmake instead of qmake from PATH. Deprecated, use qtpaths instead."_s,
257 u"path"_s
258 };
259}
260
262{
263 return {
264 u"qtpaths"_s,
265 u"Use specified qtpaths.exe instead of qtpaths.exe from PATH."_s,
266 u"path"_s
267 };
268}
269
271{
272 return {
273 u"verbose"_s,
274 u"Verbose level (0-2)."_s,
275 u"level"_s
276 };
277}
278
279static int parseEarlyArguments(const QStringList &arguments, Options *options,
280 QString *errorMessage)
281{
282 QCommandLineParser parser;
283 parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
284
285 QCommandLineOption qmakeOption = createQMakeOption();
286 parser.addOption(qmakeOption);
287
288 QCommandLineOption qtpathsOption = createQtPathsOption();
289 parser.addOption(qtpathsOption);
290
291 QCommandLineOption verboseOption = createVerboseOption();
292 parser.addOption(verboseOption);
293
294 // Deliberately don't check for errors. We want to ignore options we don't know about.
295 parser.parse(arguments);
296
297 if (parser.isSet(qmakeOption) && parser.isSet(qtpathsOption)) {
298 *errorMessage = QStringLiteral("-qmake and -qtpaths are mutually exclusive.");
300 }
301
302 if (parser.isSet(qmakeOption) && optVerboseLevel >= 1)
303 std::wcerr << "Warning: -qmake option is deprecated. Use -qtpaths instead.\n";
304
305 if (parser.isSet(qtpathsOption) || parser.isSet(qmakeOption)) {
306 const QString qtpathsArg = parser.isSet(qtpathsOption) ? parser.value(qtpathsOption)
307 : parser.value(qmakeOption);
308
309 const QString qtpathsBinary = QDir::cleanPath(qtpathsArg);
310 const QFileInfo fi(qtpathsBinary);
311 if (!fi.exists()) {
312 *errorMessage = msgFileDoesNotExist(qtpathsBinary);
314 }
315
316 if (!fi.isExecutable()) {
317 *errorMessage = u'"' + QDir::toNativeSeparators(qtpathsBinary)
318 + QStringLiteral("\" is not an executable.");
320 }
321 options->qtpathsBinary = qtpathsBinary;
322 }
323
324 if (parser.isSet(verboseOption)) {
325 bool ok;
326 const QString value = parser.value(verboseOption);
327 optVerboseLevel = value.toInt(&ok);
328 if (!ok || optVerboseLevel < 0) {
329 *errorMessage = QStringLiteral("Invalid value \"%1\" passed for verbose level.")
330 .arg(value);
332 }
333 }
334
335 return 0;
336}
337
338static inline int parseArguments(const QStringList &arguments, QCommandLineParser *parser,
339 Options *options, QString *errorMessage)
340{
341 using CommandLineOptionPtr = QSharedPointer<QCommandLineOption>;
342 using OptionPtrVector = QList<CommandLineOptionPtr>;
343
344 parser->setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
345 parser->setApplicationDescription(u"Qt Deploy Tool " QT_VERSION_STR
346 "\n\nThe simplest way to use windeployqt is to add the bin directory of your Qt\n"
347 "installation (e.g. <QT_DIR\\bin>) to the PATH variable and then run:\n windeployqt <path-to-app-binary>\n\n"
348 "If your application uses Qt Quick, run:\n windeployqt --qmldir <path-to-app-qml-files> <path-to-app-binary>"_s);
349 const QCommandLineOption helpOption = parser->addHelpOption();
350 const QCommandLineOption versionOption = parser->addVersionOption();
351
352 QCommandLineOption dirOption(QStringLiteral("dir"),
353 QStringLiteral("Use directory instead of binary directory."),
354 QStringLiteral("directory"));
355 parser->addOption(dirOption);
356
357 // Add early options to have them available in the help text.
358 parser->addOption(createQMakeOption());
359 parser->addOption(createQtPathsOption());
360
361 QCommandLineOption libDirOption(QStringLiteral("libdir"),
362 QStringLiteral("Copy libraries to path."),
363 QStringLiteral("path"));
364 parser->addOption(libDirOption);
365
366 QCommandLineOption pluginDirOption(QStringLiteral("plugindir"),
367 QStringLiteral("Copy plugins to path."),
368 QStringLiteral("path"));
369 parser->addOption(pluginDirOption);
370
371 const QCommandLineOption translationDirOption(
372 u"translationdir"_s,
373 u"Copy translations to path."_s,
374 u"path"_s);
375 parser->addOption(translationDirOption);
376
377 QCommandLineOption qmlDeployDirOption(QStringLiteral("qml-deploy-dir"),
378 QStringLiteral("Copy qml files to path."),
379 QStringLiteral("path"));
380 parser->addOption(qmlDeployDirOption);
381
382 QCommandLineOption debugOption(QStringLiteral("debug"),
383 QStringLiteral("Assume debug binaries."));
384 parser->addOption(debugOption);
385 QCommandLineOption releaseOption(QStringLiteral("release"),
386 QStringLiteral("Assume release binaries."));
387 parser->addOption(releaseOption);
388 QCommandLineOption releaseWithDebugInfoOption(QStringLiteral("release-with-debug-info"),
389 QStringLiteral("Assume release binaries with debug information."));
390 releaseWithDebugInfoOption.setFlags(QCommandLineOption::HiddenFromHelp); // Deprecated by improved debug detection.
391 parser->addOption(releaseWithDebugInfoOption);
392
393 QCommandLineOption deployPdbOption(QStringLiteral("pdb"),
394 QStringLiteral("Deploy .pdb files (MSVC)."));
395 parser->addOption(deployPdbOption);
396
397 QCommandLineOption forceOption(QStringLiteral("force"),
398 QStringLiteral("Force updating files."));
399 parser->addOption(forceOption);
400
401 QCommandLineOption dryRunOption(QStringLiteral("dry-run"),
402 QStringLiteral("Simulation mode. Behave normally, but do not copy/update any files."));
403 parser->addOption(dryRunOption);
404
405 QCommandLineOption noPatchQtOption(QStringLiteral("no-patchqt"),
406 QStringLiteral("Do not patch the Qt6Core library."));
407 parser->addOption(noPatchQtOption);
408
409 QCommandLineOption ignoreErrorOption(QStringLiteral("ignore-library-errors"),
410 QStringLiteral("Ignore errors when libraries cannot be found."));
411 parser->addOption(ignoreErrorOption);
412
413 QCommandLineOption noPluginsOption(QStringLiteral("no-plugins"),
414 QStringLiteral("Skip plugin deployment."));
415 parser->addOption(noPluginsOption);
416
417 QCommandLineOption skipPluginTypesOption(QStringLiteral("skip-plugin-types"),
418 QStringLiteral("A comma-separated list of plugin types that are not deployed (qmltooling,generic)."),
419 QStringLiteral("plugin types"));
420 parser->addOption(skipPluginTypesOption);
421
422 QCommandLineOption addPluginTypesOption(QStringLiteral("add-plugin-types"),
423 QStringLiteral("A comma-separated list of plugin types that will be added to deployment (imageformats,iconengines)"),
424 QStringLiteral("plugin types"));
425 parser->addOption(addPluginTypesOption);
426
427 QCommandLineOption includePluginsOption(QStringLiteral("include-plugins"),
428 QStringLiteral("A comma-separated list of individual plugins that will be added to deployment (scene2d,qjpeg)"),
429 QStringLiteral("plugins"));
430 parser->addOption(includePluginsOption);
431
432 QCommandLineOption excludePluginsOption(QStringLiteral("exclude-plugins"),
433 QStringLiteral("A comma-separated list of individual plugins that will not be deployed (qsvg,qpdf)"),
434 QStringLiteral("plugins"));
435 parser->addOption(excludePluginsOption);
436
437 QCommandLineOption noLibraryOption(QStringLiteral("no-libraries"),
438 QStringLiteral("Skip library deployment."));
439 parser->addOption(noLibraryOption);
440
441 QCommandLineOption qmlDirOption(QStringLiteral("qmldir"),
442 QStringLiteral("Scan for QML-imports starting from directory."),
443 QStringLiteral("directory"));
444 parser->addOption(qmlDirOption);
445
446 QCommandLineOption qmlImportOption(QStringLiteral("qmlimport"),
447 QStringLiteral("Add the given path to the QML module search locations."),
448 QStringLiteral("directory"));
449 parser->addOption(qmlImportOption);
450
451 QCommandLineOption qmlImportTimeoutOption(QStringLiteral("qmlimporttimeout"),
452 QStringLiteral("Set timeout (in ms for) execution of "
453 "qmlimportscanner."),
454 QStringLiteral("timeout"));
455 parser->addOption(qmlImportTimeoutOption);
456
457 QCommandLineOption noQuickImportOption(QStringLiteral("no-quick-import"),
458 QStringLiteral("Skip deployment of Qt Quick imports."));
459 parser->addOption(noQuickImportOption);
460
461
462 QCommandLineOption translationOption(QStringLiteral("translations"),
463 QStringLiteral("A comma-separated list of languages to deploy (de,fi)."),
464 QStringLiteral("languages"));
465 parser->addOption(translationOption);
466
467 QCommandLineOption noTranslationOption(QStringLiteral("no-translations"),
468 QStringLiteral("Skip deployment of translations."));
469 parser->addOption(noTranslationOption);
470
471 QCommandLineOption noSystemD3DCompilerOption(QStringLiteral("no-system-d3d-compiler"),
472 QStringLiteral("Skip deployment of the system D3D compiler."));
473 parser->addOption(noSystemD3DCompilerOption);
474
475 QCommandLineOption noSystemDxcOption(QStringLiteral("no-system-dxc-compiler"),
476 QStringLiteral("Skip deployment of the system DXC (dxcompiler.dll, dxil.dll)."));
477 parser->addOption(noSystemDxcOption);
478
479
480 QCommandLineOption compilerRunTimeOption(QStringLiteral("compiler-runtime"),
481 QStringLiteral("Deploy compiler runtime (Desktop only)."));
482 parser->addOption(compilerRunTimeOption);
483
484 QCommandLineOption noCompilerRunTimeOption(QStringLiteral("no-compiler-runtime"),
485 QStringLiteral("Do not deploy compiler runtime (Desktop only)."));
486 parser->addOption(noCompilerRunTimeOption);
487
488 QCommandLineOption jsonOption(QStringLiteral("json"),
489 QStringLiteral("Print to stdout in JSON format."));
490 parser->addOption(jsonOption);
491
492 QCommandLineOption suppressSoftwareRasterizerOption(QStringLiteral("no-opengl-sw"),
493 QStringLiteral("Do not deploy the software rasterizer library."));
494 parser->addOption(suppressSoftwareRasterizerOption);
495
496 QCommandLineOption noFFmpegOption(QStringLiteral("no-ffmpeg"),
497 QStringLiteral("Do not deploy the FFmpeg libraries."));
498 parser->addOption(noFFmpegOption);
499
500 QCommandLineOption noThirdPartyLibrariesOption(
501 QStringLiteral("no-third-party-libraries"),
502 QStringLiteral("Do not deploy the non-Qt libraries from the Qt binary directory "
503 "that the deployed binaries link against."));
504 parser->addOption(noThirdPartyLibrariesOption);
505
506 QCommandLineOption forceOpenSslOption(QStringLiteral("force-openssl"),
507 QStringLiteral("Deploy openssl plugin but ignore openssl library dependency"));
508 parser->addOption(forceOpenSslOption);
509
510 QCommandLineOption openSslRootOption(QStringLiteral("openssl-root"),
511 QStringLiteral("Directory containing openSSL libraries."),
512 QStringLiteral("directory"));
513 parser->addOption(openSslRootOption);
514
515
516 QCommandLineOption listOption(QStringLiteral("list"),
517 "Print only the names of the files copied.\n"
518 "Available options:\n"
519 " source: absolute path of the source files\n"
520 " target: absolute path of the target files\n"
521 " relative: paths of the target files, relative\n"
522 " to the target directory\n"
523 " mapping: outputs the source and the relative\n"
524 " target, suitable for use within an\n"
525 " Appx mapping file"_L1,
526 QStringLiteral("option"));
527 parser->addOption(listOption);
528
529 QCommandLineOption appxOption(QStringLiteral("appx"),
530 QStringLiteral("Create an appx package for the Windows Store"));
531 parser->addOption(appxOption);
532
533 QCommandLineOption appxCertificatePath(QStringLiteral("appx-certificate"),
534 QStringLiteral("Path to appx certificate to sign appx package"),
535 QStringLiteral(".cer file"));
536 parser->addOption(appxCertificatePath);
537
538 // Add early option to have it available in the help text.
539 parser->addOption(createVerboseOption());
540
541 parser->addPositionalArgument(QStringLiteral("[files]"),
542 QStringLiteral("Binaries or directory containing the binary."));
543
544 QCommandLineOption deployInsightTrackerOption(QStringLiteral("deploy-insighttracker"),
545 QStringLiteral("Deploy insight tracker plugin."));
546 // The option will be added to the parser if the module is available (see block below)
547 bool insightTrackerModuleAvailable = false;
548
549 OptionPtrVector enabledModuleOptions;
550 OptionPtrVector disabledModuleOptions;
551 const size_t qtModulesCount = qtModuleEntries.size();
552 enabledModuleOptions.reserve(qtModulesCount);
553 disabledModuleOptions.reserve(qtModulesCount);
554 for (const QtModule &module : qtModuleEntries) {
555 const QString option = moduleNameToOptionName(module.name, module.internal);
556 const QString name = module.name;
557 if (name == u"InsightTracker") {
558 parser->addOption(deployInsightTrackerOption);
559 insightTrackerModuleAvailable = true;
560 }
561 const QString enabledDescription = QStringLiteral("Add ") + name + QStringLiteral(" module.");
562 CommandLineOptionPtr enabledOption(new QCommandLineOption(option, enabledDescription));
563 parser->addOption(*enabledOption.data());
564 enabledModuleOptions.append(enabledOption);
565 const QString disabledDescription = QStringLiteral("Remove ") + name + QStringLiteral(" module.");
566 CommandLineOptionPtr disabledOption(new QCommandLineOption(QStringLiteral("no-") + option,
567 disabledDescription));
568 disabledModuleOptions.append(disabledOption);
569 parser->addOption(*disabledOption.data());
570 }
571
572 const bool success = parser->parse(arguments);
573 if (parser->isSet(helpOption))
575 if (parser->isSet(versionOption))
577 if (!success) {
578 *errorMessage = parser->errorText();
580 }
581
582 options->libraryDirectory = parser->value(libDirOption);
583 options->pluginDirectory = parser->value(pluginDirOption);
584 options->translationsDirectory = parser->value(translationDirOption);
585 options->qmlDirectory = parser->value(qmlDeployDirOption);
586 options->plugins = !parser->isSet(noPluginsOption);
587 options->libraries = !parser->isSet(noLibraryOption);
588 options->translations = !parser->isSet(noTranslationOption);
589 if (parser->isSet(translationOption))
590 options->languages = parser->value(translationOption).split(u',');
591 options->systemD3dCompiler = !parser->isSet(noSystemD3DCompilerOption);
592 options->systemDxc = !parser->isSet(noSystemDxcOption);
593 options->quickImports = !parser->isSet(noQuickImportOption);
594 options->createAppx = parser->isSet(appxOption);
595 if (options->createAppx) {
596 if (!parser->isSet(appxCertificatePath)) {
597 *errorMessage = QStringLiteral("--appx requires --appx-certificate with a valid certificate");
599 }
600 options->appxCertificatePath = parser->value(appxCertificatePath);
601 }
602
603 // default to deployment of compiler runtime for windows desktop configurations
604 if (options->platform == WindowsDesktopMinGW || options->platform == WindowsDesktopClangMinGW
605 || options->platform.testFlags(WindowsDesktopMsvc) || parser->isSet(compilerRunTimeOption))
606 options->compilerRunTime = true;
607 if (parser->isSet(noCompilerRunTimeOption))
608 options->compilerRunTime = false;
609
610 if (options->compilerRunTime && options->platform != WindowsDesktopMinGW
611 && options->platform != WindowsDesktopClangMinGW
612 && !options->platform.testFlags(WindowsDesktopMsvc)) {
613 *errorMessage = QStringLiteral("Deployment of the compiler runtime is implemented for "
614 "Desktop MSVC and MinGW (g++ and Clang) only.");
616 }
617
618 if (parser->isSet(skipPluginTypesOption))
619 options->pluginSelections.disabledPluginTypes = parser->value(skipPluginTypesOption).split(u',');
620
621 if (parser->isSet(addPluginTypesOption))
622 options->pluginSelections.enabledPluginTypes = parser->value(addPluginTypesOption).split(u',');
623
624 if (parser->isSet(includePluginsOption))
625 options->pluginSelections.includedPlugins = parser->value(includePluginsOption).split(u',');
626
627 if (parser->isSet(excludePluginsOption))
628 options->pluginSelections.excludedPlugins = parser->value(excludePluginsOption).split(u',');
629
630 if (parser->isSet(releaseWithDebugInfoOption))
631 std::wcerr << "Warning: " << releaseWithDebugInfoOption.names().first() << " is obsolete.";
632
633 switch (parseExclusiveOptions(parser, debugOption, releaseOption)) {
634 case OptionAuto:
635 break;
636 case OptionEnabled:
638 break;
639 case OptionDisabled:
641 break;
642 }
643
644 if (parser->isSet(deployPdbOption)) {
645 if (options->platform.testFlag(WindowsBased) && !options->platform.testFlag(MinGW))
646 options->deployPdb = true;
647 else
648 std::wcerr << "Warning: --" << deployPdbOption.names().first() << " is not supported on this platform.\n";
649 }
650
651 if (parser->isSet(suppressSoftwareRasterizerOption))
652 options->softwareRasterizer = false;
653
654 if (parser->isSet(noFFmpegOption))
655 options->ffmpeg = false;
656
657 if (parser->isSet(noThirdPartyLibrariesOption))
658 options->thirdPartyLibraries = false;
659
660 if (parser->isSet(forceOpenSslOption))
661 options->forceOpenSslPlugin = true;
662
663 if (parser->isSet(openSslRootOption))
664 options->openSslRootDirectory = parser->value(openSslRootOption);
665
666 if (options->forceOpenSslPlugin && !options->openSslRootDirectory.isEmpty()) {
667 *errorMessage = QStringLiteral("force-openssl and openssl-root are mutually exclusive");
669 }
670
671 if (parser->isSet(forceOption))
673 if (parser->isSet(dryRunOption)) {
674 options->dryRun = true;
676 }
677
678 options->patchQt = !parser->isSet(noPatchQtOption);
679 options->ignoreLibraryErrors = parser->isSet(ignoreErrorOption);
680 if (insightTrackerModuleAvailable)
681 options->deployInsightTrackerPlugin = parser->isSet(deployInsightTrackerOption);
682
683 for (const QtModule &module : qtModuleEntries) {
684 if (parser->isSet(*enabledModuleOptions.at(module.id)))
685 options->additionalLibraries[module.id] = 1;
686 if (parser->isSet(*disabledModuleOptions.at(module.id)))
687 options->disabledLibraries[module.id] = 1;
688 }
689
690 // Add some dependencies
691 if (options->additionalLibraries.test(QtQuickModuleId))
692 options->additionalLibraries[QtQmlModuleId] = 1;
693 if (options->additionalLibraries.test(QtDesignerComponentsModuleId))
694 options->additionalLibraries[QtDesignerModuleId] = 1;
695
696 if (parser->isSet(listOption)) {
697 const QString value = parser->value(listOption);
698 if (value == QStringLiteral("source")) {
699 options->list = ListSource;
700 } else if (value == QStringLiteral("target")) {
701 options->list = ListTarget;
702 } else if (value == QStringLiteral("relative")) {
703 options->list = ListRelative;
704 } else if (value == QStringLiteral("mapping")) {
705 options->list = ListMapping;
706 } else {
707 *errorMessage = QStringLiteral("Please specify a valid option for -list (source, target, relative, mapping).");
709 }
710 }
711
712 if (parser->isSet(jsonOption) || options->list) {
713 optVerboseLevel = 0;
714 options->json = new JsonOutput;
715 }
716
717 const QStringList posArgs = parser->positionalArguments();
718 if (posArgs.isEmpty()) {
719 *errorMessage = QStringLiteral("Please specify the binary or folder.");
721 }
722
723 if (parser->isSet(dirOption))
724 options->directory = parser->value(dirOption);
725
726 if (parser->isSet(qmlDirOption))
727 options->qmlDirectories = parser->values(qmlDirOption);
728
729 if (parser->isSet(qmlImportOption))
730 options->qmlImportPaths = parser->values(qmlImportOption);
731
732 if (parser->isSet(qmlImportTimeoutOption)) {
733 const QString timeoutString = parser->value(qmlImportTimeoutOption);
734 bool ok;
735 int timeout = timeoutString.toInt(&ok);
736 if (!ok) {
737 *errorMessage = u'"' + timeoutString + QStringLiteral("\" is not an acceptable timeout "
738 "value.");
740 }
741 options->qmlImportTimeout = timeout;
742 }
743
744 const QString &file = posArgs.front();
745 const QFileInfo fi(QDir::cleanPath(file));
746 if (!fi.exists()) {
747 *errorMessage = msgFileDoesNotExist(file);
749 }
750
751 if (!options->directory.isEmpty() && !fi.isFile()) { // -dir was specified - expecting file.
752 *errorMessage = u'"' + file + QStringLiteral("\" is not an executable file.");
754 }
755
756 if (fi.isFile()) {
757 options->binaries.append(fi.absoluteFilePath());
758 if (options->directory.isEmpty())
759 options->directory = fi.absolutePath();
760 } else {
761 const QString binary = findBinary(fi.absoluteFilePath(), options->platform);
762 if (binary.isEmpty()) {
763 *errorMessage = QStringLiteral("Unable to find binary in \"") + file + u'"';
765 }
766 options->directory = fi.absoluteFilePath();
767 options->binaries.append(binary);
768 } // directory.
769
770 // Remaining files or plugin directories
771 bool multipleDirs = false;
772 for (int i = 1; i < posArgs.size(); ++i) {
773 const QFileInfo fi(QDir::cleanPath(posArgs.at(i)));
774 const QString path = fi.absoluteFilePath();
775 if (!fi.exists()) {
776 *errorMessage = msgFileDoesNotExist(path);
778 }
779 if (fi.isDir()) {
780 const QStringList libraries =
781 findSharedLibraries(QDir(path), options->platform, MatchDebugOrRelease, QString());
782 for (const QString &library : libraries)
783 options->binaries.append(path + u'/' + library);
784 } else {
785 if (!parser->isSet(dirOption) && fi.absolutePath() != options->directory)
786 multipleDirs = true;
787 options->binaries.append(path);
788 }
789 }
790 if (multipleDirs) {
791 std::wcerr << "Warning: using binaries from different directories, deploying to following path: "
792 << options->directory << '\n' << "To disable this warning, use the --dir option\n";
793 }
794 if (options->translationsDirectory.isEmpty())
795 options->translationsDirectory = options->directory + "/translations"_L1;
796 return 0;
797}
798
799// Simple line wrapping at 80 character boundaries.
800static inline QString lineBreak(QString s)
801{
802 for (qsizetype i = 80; i < s.size(); i += 80) {
803 const qsizetype lastBlank = s.lastIndexOf(u' ', i);
804 if (lastBlank >= 0) {
805 s[lastBlank] = u'\n';
806 i = lastBlank + 1;
807 }
808 }
809 return s;
810}
811
812static inline QString helpText(const QCommandLineParser &p, const PluginInformation &pluginInfo)
813{
814 QString result = p.helpText();
815 // Replace the default-generated text which is too long by a short summary
816 // explaining how to enable single libraries.
817 if (qtModuleEntries.size() == 0)
818 return result;
819 const QtModule &firstModule = qtModuleEntries.moduleById(0);
820 const QString firstModuleOption = moduleNameToOptionName(firstModule.name, firstModule.internal);
821 const qsizetype moduleStart = result.indexOf("\n --"_L1 + firstModuleOption);
822 const qsizetype argumentsStart = result.lastIndexOf("\nArguments:"_L1);
823 if (moduleStart >= argumentsStart)
824 return result;
825 QString moduleHelp;
826 moduleHelp +=
827 "\n\nQt libraries can be added by passing their name (-xml) or removed by passing\n"
828 "the name prepended by --no- (--no-xml). Available libraries:\n"_L1;
829 ModuleBitset mask;
830 moduleHelp += lineBreak(QString::fromLatin1(formatQtModules(mask.set(), true)));
831 moduleHelp += u"\n\n";
832 moduleHelp +=
833 u"Qt plugins can be included or excluded individually or by type.\n"
834 u"To deploy or block plugins individually, use the --include-plugins\n"
835 u"and --exclude-plugins options (--include-plugins qjpeg,qsvgicon)\n"
836 u"You can also use the --skip-plugin-types or --add-plugin-types to\n"
837 u"achieve similar results with entire plugin groups, like imageformats, e.g.\n"
838 u"(--add-plugin-types imageformats,iconengines). Exclusion always takes\n"
839 u"precedence over inclusion, and types take precedence over specific plugins.\n"
840 u"For example, including qjpeg, but skipping imageformats, will NOT deploy qjpeg.\n"
841 u"\nDetected available plugins:\n";
842 moduleHelp += formatQtPlugins(pluginInfo);
843 result.replace(moduleStart, argumentsStart - moduleStart, moduleHelp);
844 return result;
845}
846
847static inline bool isQtModule(const QString &libName)
848{
849 // Match Standard modules named Qt6XX.dll
850 if (libName.size() < 3 || !libName.startsWith("Qt"_L1, Qt::CaseInsensitive))
851 return false;
852 const QChar version = libName.at(2);
853 return version.isDigit() && (version.toLatin1() - '0') == QT_VERSION_MAJOR;
854}
855
856// Helper for recursively finding all dependent Qt libraries.
857static bool findDependentQtLibraries(const QString &qtBinDir, const QString &binary,
858 Platform platform, QString *errorMessage,
859 QStringList *qtDependencies, QStringList *nonQtDependencies)
860{
861 QStringList dependentLibs;
862 if (!readPeExecutableDependencies(binary, errorMessage, &dependentLibs)) {
863 errorMessage->prepend("Unable to find dependent libraries of "_L1 +
864 QDir::toNativeSeparators(binary) + " :"_L1);
865 return false;
866 }
867 // Filter out the Qt libraries. Note that depends.exe finds libs from optDirectory if we
868 // are run the 2nd time (updating). We want to check against the Qt bin dir libraries
869 const int start = qtDependencies->size();
870 for (const QString &lib : std::as_const(dependentLibs)) {
871 if (isQtModule(lib)) {
872 const QString path = normalizeFileName(qtBinDir + u'/' + QFileInfo(lib).fileName());
873 if (!qtDependencies->contains(path))
874 qtDependencies->append(path);
875 } else if (nonQtDependencies && !nonQtDependencies->contains(lib)) {
876 nonQtDependencies->append(lib);
877 }
878 }
879 const int end = qtDependencies->size();
880 // Recurse
881 for (int i = start; i < end; ++i) {
882 if (!findDependentQtLibraries(qtBinDir, qtDependencies->at(i), platform, errorMessage,
883 qtDependencies, nonQtDependencies)) {
884 return false;
885 }
886 }
887 return true;
888}
889
890// Base class to filter debug/release Windows DLLs for functions to be passed to updateFile().
891// Tries to pre-filter by namefilter and does check via PE.
893public:
894 explicit DllDirectoryFileEntryFunction(Platform platform,
895 DebugMatchMode debugMatchMode, const QString &prefix = QString()) :
896 m_platform(platform), m_debugMatchMode(debugMatchMode), m_prefix(prefix) {}
897
898 QStringList operator()(const QDir &dir) const
899 { return findSharedLibraries(dir, m_platform, m_debugMatchMode, m_prefix); }
900
901private:
902 const Platform m_platform;
903 const DebugMatchMode m_debugMatchMode;
904 const QString m_prefix;
905};
906
907static QString pdbFileName(QString libraryFileName)
908{
909 const qsizetype lastDot = libraryFileName.lastIndexOf(u'.') + 1;
910 if (lastDot <= 0)
911 return QString();
912 libraryFileName.replace(lastDot, libraryFileName.size() - lastDot, "pdb"_L1);
913 return libraryFileName;
914}
916{
917 return QStringList() << QStringLiteral("*.jsc") << QStringLiteral("*.qmlc");
918}
919
920// File entry filter function for updateFile() that returns a list of files for
921// QML import trees: DLLs (matching debug) and .qml/,js, etc.
923public:
924 enum Flags {
927 };
928
930 const QString &moduleSourcePath, Platform platform, DebugMatchMode debugMatchMode, unsigned flags)
933 {}
934
935 QStringList operator()(const QDir &dir) const
936 {
937 if (moduleSourceDir(dir).canonicalPath() != m_moduleSourcePath) {
938 // If we're in a different module, return nothing.
939 return {};
940 }
941
942 QStringList result;
943 const QStringList &libraries = m_dllFilter(dir);
944 for (const QString &library : libraries) {
945 result.append(library);
946 if (m_flags & DeployPdb) {
947 const QString pdb = pdbFileName(library);
948 if (QFileInfo(dir.absoluteFilePath(pdb)).isFile())
949 result.append(pdb);
950 }
951 }
952 result.append(m_qmlNameFilter(dir));
953 return result;
954 }
955
956private:
957 static QDir moduleSourceDir(const QDir &dir)
958 {
959 QDir moduleSourceDir = dir;
960 while (!moduleSourceDir.exists(QStringLiteral("qmldir"))) {
961 if (!moduleSourceDir.cdUp()) {
962 return {};
963 }
964 }
965 return moduleSourceDir;
966 }
967
968 static inline QStringList qmlNameFilters(unsigned flags)
969 {
970 QStringList result;
971 result << QStringLiteral("qmldir") << QStringLiteral("*.qmltypes")
972 << QStringLiteral("*.frag") << QStringLiteral("*.vert") // Shaders
973 << QStringLiteral("*.ttf");
974 if (!(flags & SkipSources)) {
975 result << QStringLiteral("*.js") << QStringLiteral("*.qml") << QStringLiteral("*.png");
976 result.append(qmlCacheFileFilters());
977 }
978 return result;
979 }
980
981 const unsigned m_flags;
982 NameFilterFileEntryFunction m_qmlNameFilter;
984 QString m_moduleSourcePath;
985};
986
987static qint64 qtModule(QString module, const QString &infix)
988{
989 // Match needle 'path/Qt6Core<infix><d>.dll' or 'path/libQt6Core<infix>.so.5.0'
990 const qsizetype lastSlashPos = module.lastIndexOf(u'/');
991 if (lastSlashPos > 0)
992 module.remove(0, lastSlashPos + 1);
993 if (module.startsWith("lib"_L1))
994 module.remove(0, 3);
995 int endPos = infix.isEmpty() ? -1 : module.lastIndexOf(infix);
996 if (endPos == -1)
997 endPos = module.indexOf(u'.'); // strip suffixes '.so.5.0'.
998 if (endPos > 0)
999 module.truncate(endPos);
1000 // That should leave us with 'Qt6Core<d>'.
1001 for (const auto &qtModule : qtModuleEntries) {
1002 const QString &libraryName = qtModule.name;
1003 if (module == libraryName
1004 || (module.size() == libraryName.size() + 1 && module.startsWith(libraryName))) {
1005 return qtModule.id;
1006 }
1007 }
1008 std::wcerr << "Warning: module " << qPrintable(module) << " could not be found\n";
1009 return -1;
1010}
1011
1012// Return the path if a plugin is to be deployed
1013static QString deployPlugin(const QString &plugin, const QDir &subDir, const bool dueToModule,
1014 const DebugMatchMode &debugMatchMode, ModuleBitset *pluginNeededQtModules,
1015 const ModuleBitset &disabledQtModules,
1016 const PluginSelections &pluginSelections, const QString &libraryLocation,
1017 const QString &infix, Platform platform,
1018 bool deployInsightTrackerPlugin, bool deployOpenSslPlugin)
1019{
1020 const QString subDirName = subDir.dirName();
1021 // Filter out disabled plugins
1022 if (pluginSelections.disabledPluginTypes.contains(subDirName)) {
1023 if (optVerboseLevel) {
1024 std::wcout << "Skipping plugin " << plugin << " due to skipped plugin type "
1025 << subDirName << '\n';
1026 }
1027 return {};
1028 }
1029 if (subDirName == u"generic" && plugin.contains(u"qinsighttracker")
1030 && !deployInsightTrackerPlugin) {
1031 if (optVerboseLevel) {
1032 std::wcout << "Skipping plugin " << plugin
1033 << ". Use -deploy-insighttracker if you want to use it.\n";
1034 }
1035 return {};
1036 }
1037 if (subDirName == u"tls" && plugin.contains(u"qopensslbackend")
1038 && !deployOpenSslPlugin) {
1039 if (optVerboseLevel) {
1040 std::wcout << "Skipping plugin " << plugin
1041 << ". Use -force-openssl or specify -openssl-root if you want to use it.\n";
1042 }
1043 return {};
1044 }
1045
1046 const int dotIndex = plugin.lastIndexOf(u'.');
1047 // Strip the .dll from the name, and an additional 'd' if it's a debug library with the 'd'
1048 // suffix
1049 const int stripIndex = debugMatchMode == MatchDebug && platformHasDebugSuffix(platform)
1050 ? dotIndex - 1
1051 : dotIndex;
1052 const QString pluginName = plugin.first(stripIndex);
1053
1054 if (pluginSelections.excludedPlugins.contains(pluginName)) {
1055 if (optVerboseLevel)
1056 std::wcout << "Skipping plugin " << plugin << " due to exclusion option" << '\n';
1057 return {};
1058 }
1059
1060 // By default, only deploy qwindows.dll
1061 if (subDirName == u"platforms"
1062 && !(pluginSelections.includedPlugins.contains(pluginName)
1063 || (pluginSelections.enabledPluginTypes.contains(subDirName)))
1064 && !pluginName.startsWith(u"qwindows")) {
1065 return {};
1066 }
1067
1068 const QString pluginPath = subDir.absoluteFilePath(plugin);
1069
1070 // If dueToModule is false, check if the user included the plugin or the entire type. In the
1071 // former's case, only deploy said plugin and not all plugins of that type.
1072 const bool requiresPlugin = pluginSelections.includedPlugins.contains(pluginName)
1073 || pluginSelections.enabledPluginTypes.contains(subDirName);
1074 if (!dueToModule && !requiresPlugin)
1075 return {};
1076
1077 // Deploy QUiTools plugins as is without further dependency checking.
1078 // The user needs to ensure all required libraries are present (would
1079 // otherwise pull QtWebEngine for its plugin).
1080 if (subDirName == u"designer")
1081 return pluginPath;
1082
1083 QStringList dependentQtLibs;
1084 QString errorMessage;
1085 if (findDependentQtLibraries(libraryLocation, pluginPath, platform,
1086 &errorMessage, &dependentQtLibs, nullptr)) {
1087 for (int d = 0; d < dependentQtLibs.size(); ++d) {
1088 const qint64 module = qtModule(dependentQtLibs.at(d), infix);
1089 if (module >= 0)
1090 (*pluginNeededQtModules)[module] = 1;
1091 }
1092 } else {
1093 std::wcerr << "Warning: Cannot determine dependencies of "
1094 << QDir::toNativeSeparators(pluginPath) << ": " << errorMessage << '\n';
1095 }
1096
1097 ModuleBitset disabledNeededQtModules;
1098 disabledNeededQtModules = *pluginNeededQtModules & disabledQtModules;
1099 if (disabledNeededQtModules.any()) {
1100 if (optVerboseLevel) {
1101 std::wcout << "Skipping plugin " << plugin
1102 << " due to disabled dependencies ("
1103 << formatQtModules(disabledNeededQtModules).constData() << ").\n";
1104 }
1105 return {};
1106 }
1107
1108 return pluginPath;
1109}
1110
1111static bool needsPluginType(const QString &subDirName, const PluginInformation &pluginInfo,
1112 const PluginSelections &pluginSelections)
1113{
1114 bool needsTypeForPlugin = false;
1115 for (const QString &plugin: pluginSelections.includedPlugins) {
1116 if (pluginInfo.isTypeForPlugin(subDirName, plugin))
1117 needsTypeForPlugin = true;
1118 }
1119 return (pluginSelections.enabledPluginTypes.contains(subDirName) || needsTypeForPlugin);
1120}
1121
1122QStringList findQtPlugins(ModuleBitset *usedQtModules, const ModuleBitset &disabledQtModules,
1123 const PluginInformation &pluginInfo, const PluginSelections &pluginSelections,
1124 const QString &qtPluginsDirName, const QString &libraryLocation,
1125 const QString &infix, DebugMatchMode debugMatchModeIn, Platform platform,
1126 QString *platformPlugin, bool deployInsightTrackerPlugin,
1127 bool deployOpenSslPlugin)
1128{
1129 if (qtPluginsDirName.isEmpty())
1130 return QStringList();
1131 QDir pluginsDir(qtPluginsDirName);
1132 QStringList result;
1133 bool missingQtModulesAdded = false;
1134 const QFileInfoList &pluginDirs = pluginsDir.entryInfoList(QStringList(u"*"_s), QDir::Dirs | QDir::NoDotAndDotDot);
1135 for (const QFileInfo &subDirFi : pluginDirs) {
1136 const QString subDirName = subDirFi.fileName();
1137 const size_t module = qtModuleEntries.moduleIdForPluginType(subDirName);
1138 if (module == QtModule::InvalidId) {
1139 if (optVerboseLevel > 1) {
1140 std::wcerr << "No Qt module found for plugin type \"" << subDirName << "\".\n";
1141 }
1142 continue;
1143 }
1144 const bool dueToModule = usedQtModules->test(module);
1145 if (dueToModule || needsPluginType(subDirName, pluginInfo, pluginSelections)) {
1146 const DebugMatchMode debugMatchMode = (module == QtWebEngineCoreModuleId)
1147 ? MatchDebugOrRelease // QTBUG-44331: Debug detection does not work for webengine, deploy all.
1148 : debugMatchModeIn;
1149 QDir subDir(subDirFi.absoluteFilePath());
1150 if (optVerboseLevel)
1151 std::wcout << "Adding in plugin type " << subDirFi.baseName() << " for module: " << qtModuleEntries.moduleById(module).name << '\n';
1152
1153 const bool isPlatformPlugin = subDirName == "platforms"_L1;
1154 const QStringList plugins =
1155 findSharedLibraries(subDir, platform, debugMatchMode, QString());
1156 for (const QString &plugin : plugins) {
1157 ModuleBitset pluginNeededQtModules;
1158 const QString pluginPath =
1159 deployPlugin(plugin, subDir, dueToModule, debugMatchMode, &pluginNeededQtModules,
1160 disabledQtModules, pluginSelections, libraryLocation, infix,
1161 platform, deployInsightTrackerPlugin, deployOpenSslPlugin);
1162 if (!pluginPath.isEmpty()) {
1163 if (isPlatformPlugin && plugin.startsWith(u"qwindows"))
1164 *platformPlugin = subDir.absoluteFilePath(plugin);
1165 result.append(pluginPath);
1166
1167 const ModuleBitset missingModules = (pluginNeededQtModules & ~*usedQtModules);
1168 if (missingModules.any()) {
1169 *usedQtModules |= missingModules;
1170 missingQtModulesAdded = true;
1171 if (optVerboseLevel) {
1172 std::wcout << "Adding " << formatQtModules(missingModules).constData()
1173 << " for " << plugin << " from plugin type: " << subDirName << '\n';
1174 }
1175 }
1176 }
1177 } // for filter
1178 } // type matches
1179 } // for plugin folder
1180
1181 // If missing Qt modules were added during plugin deployment make additional pass, because we may need
1182 // additional plugins.
1183 if (missingQtModulesAdded) {
1184 if (optVerboseLevel) {
1185 std::wcout << "Performing additional pass of finding Qt plugins due to updated Qt module list: "
1186 << formatQtModules(*usedQtModules).constData() << "\n";
1187 }
1188 return findQtPlugins(usedQtModules, disabledQtModules, pluginInfo, pluginSelections, qtPluginsDirName,
1189 libraryLocation, infix, debugMatchModeIn, platform, platformPlugin,
1190 deployInsightTrackerPlugin, deployOpenSslPlugin);
1191 }
1192
1193 return result;
1194}
1195
1196static QStringList translationNameFilters(const ModuleBitset &modules, const QString &prefix)
1197{
1198 QStringList result;
1199 for (const auto &qtModule : qtModuleEntries) {
1200 if (modules.test(qtModule.id) && !qtModule.translationCatalog.isEmpty()) {
1201 const QString name = qtModule.translationCatalog + u'_' + prefix + ".qm"_L1;
1202 if (!result.contains(name))
1203 result.push_back(name);
1204 }
1205 }
1206 return result;
1207}
1208
1209static bool deployTranslations(const QString &sourcePath, const ModuleBitset &usedQtModules,
1210 const QString &target, const Options &options,
1211 QString *errorMessage)
1212{
1213 // Find available languages prefixes by checking on qtbase.
1214 QStringList prefixes;
1215 QDir sourceDir(sourcePath);
1216 const QStringList qmFilter = QStringList(QStringLiteral("qtbase_*.qm"));
1217 const QFileInfoList &qmFiles = sourceDir.entryInfoList(qmFilter);
1218 for (const QFileInfo &qmFi : qmFiles) {
1219 const QString prefix = qmFi.baseName().mid(7);
1220 if (options.languages.isEmpty() || options.languages.contains(prefix))
1221 prefixes.append(prefix);
1222 }
1223 if (prefixes.isEmpty()) {
1224 std::wcerr << "Warning: Could not find any translations in "
1225 << QDir::toNativeSeparators(sourcePath) << " (developer build?)\n.";
1226 return true;
1227 }
1228 // Run lconvert to concatenate all files into a single named "qt_<prefix>.qm" in the application folder
1229 // Use QT_INSTALL_TRANSLATIONS as working directory to keep the command line short.
1230 const QString absoluteTarget = QFileInfo(target).absoluteFilePath();
1231 const QString binary = QStringLiteral("lconvert");
1232 QStringList arguments;
1233 for (const QString &prefix : std::as_const(prefixes)) {
1234 arguments.clear();
1235 const QString targetFile = QStringLiteral("qt_") + prefix + QStringLiteral(".qm");
1236 arguments.append(QStringLiteral("-o"));
1237 const QString targetFilePath = absoluteTarget + u'/' + targetFile;
1238 if (options.json)
1239 options.json->addFile(sourcePath + u'/' + targetFile, absoluteTarget);
1240 arguments.append(QDir::toNativeSeparators(targetFilePath));
1241 const QStringList translationFilters = translationNameFilters(usedQtModules, prefix);
1242 if (translationFilters.isEmpty()){
1243 std::wcerr << "Warning: translation catalogs are all empty, skipping translation deployment\n";
1244 return true;
1245 }
1246 const QFileInfoList &langQmFiles = sourceDir.entryInfoList(translationFilters);
1247 for (const QFileInfo &langQmFileFi : langQmFiles) {
1248 if (options.json) {
1249 options.json->addFile(langQmFileFi.absoluteFilePath(),
1250 absoluteTarget);
1251 }
1252 arguments.append(langQmFileFi.fileName());
1253 }
1254 if (optVerboseLevel)
1255 std::wcout << "Creating " << targetFile << "...\n";
1256 unsigned long exitCode;
1257 if ((options.updateFileFlags & SkipUpdateFile) == 0
1258 && (!runProcess(binary, arguments, sourcePath, &exitCode, nullptr, nullptr, errorMessage)
1259 || exitCode)) {
1260 return false;
1261 }
1262 } // for prefixes.
1263 return true;
1264}
1265
1266static constexpr std::array ffmpegHints = { "avcodec"_L1, "avformat"_L1, "avutil"_L1,
1267 "swresample"_L1, "swscale"_L1 };
1268
1269static bool isFFmpegLibrary(const QString &libraryFileName)
1270{
1271 return std::any_of(ffmpegHints.cbegin(), ffmpegHints.cend(),
1272 [&libraryFileName](QLatin1StringView hint) {
1273 return libraryFileName.contains(hint, Qt::CaseInsensitive);
1274 });
1275}
1276
1277// Find the non-Qt libraries in the Qt binary directory that the given binaries link against.
1278// Qt builds that use system libraries instead of the bundled ones - for instance Qt from a
1279// package manager - have those libraries next to the Qt libraries rather than linked into them.
1280static bool findThirdPartyDependencies(const QString &qtBinDir, const QStringList &binaries,
1281 bool excludeFFmpeg, QSet<QString> excludedFileNames,
1282 QStringList *thirdPartyLibraries, QString *errorMessage)
1283{
1284 QStringList queue = binaries;
1285 QSet<QString> visited;
1286 while (!queue.isEmpty()) {
1287 const QString binary = queue.takeFirst();
1288 // The libraries to deploy are not all guaranteed to exist, for instance the debug
1289 // libraries of a release-only Qt. Leave reporting those to updateLibrary().
1290 if (!QFileInfo(binary).isFile())
1291 continue;
1292 const QString key = normalizeFileName(binary).toLower();
1293 if (visited.contains(key))
1294 continue;
1295 visited.insert(key);
1296 QStringList dependentLibs;
1297 if (!readPeExecutableDependencies(binary, errorMessage, &dependentLibs)) {
1298 errorMessage->prepend("Unable to find dependent libraries of "_L1
1299 + QDir::toNativeSeparators(binary) + " :"_L1);
1300 return false;
1301 }
1302 for (const QString &lib : std::as_const(dependentLibs)) {
1303 if (isQtModule(lib))
1304 continue;
1305 if (excludedFileNames.contains(lib.toLower()))
1306 continue;
1307 if (excludeFFmpeg && isFFmpegLibrary(lib))
1308 continue;
1309 const QFileInfo fi(qtBinDir + u'/' + lib);
1310 if (!fi.isFile())
1311 continue;
1312 const QString path = normalizeFileName(fi.absoluteFilePath());
1313 excludedFileNames.insert(QFileInfo(path).fileName().toLower());
1314 thirdPartyLibraries->append(path);
1315 queue.append(path);
1316 }
1317 }
1318 return true;
1319}
1320
1321static QStringList findFFmpegLibs(const QString &qtBinDir, Platform platform)
1322{
1323 const QStringList bundledLibs =
1324 findSharedLibraries(qtBinDir, platform, MatchDebugOrRelease, {});
1325
1326 QStringList ffmpegLibs;
1327 for (const QLatin1StringView &libHint : ffmpegHints) {
1328 const QStringList ffmpegLib = bundledLibs.filter(libHint, Qt::CaseInsensitive);
1329
1330 if (ffmpegLib.empty()) {
1331 std::wcerr << "Warning: Cannot find FFmpeg libraries. Multimedia features will not work as expected.\n";
1332 return {};
1333 } else if (ffmpegLib.size() != 1u) {
1334 std::wcerr << "Warning: Multiple versions of FFmpeg libraries found. Multimedia features will not work as expected.\n";
1335 return {};
1336 }
1337
1338 const QChar slash(u'/');
1339 QFileInfo ffmpegLibPath{ qtBinDir + slash + ffmpegLib.front() };
1340 ffmpegLibs.append(ffmpegLibPath.absoluteFilePath());
1341 }
1342
1343 return ffmpegLibs;
1344}
1345
1346// Find the openssl libraries Qt executables depend on.
1347static QStringList findOpenSslLibraries(const QString &openSslRootDir, Platform platform)
1348{
1349 const std::vector<QLatin1StringView> libHints = { "libcrypto"_L1, "libssl"_L1 };
1350 const QChar slash(u'/');
1351 const QString openSslBinDir = openSslRootDir + slash + "bin"_L1;
1352 const QStringList openSslRootLibs =
1353 findSharedLibraries(openSslBinDir, platform, MatchDebugOrRelease, {});
1354
1355 QStringList result;
1356 for (const QLatin1StringView &libHint : libHints) {
1357 const QStringList lib = openSslRootLibs.filter(libHint, Qt::CaseInsensitive);
1358
1359 if (lib.empty()) {
1360 std::wcerr << "Warning: Cannot find openssl libraries.\n";
1361 return {};
1362 } else if (lib.size() != 1u) {
1363 std::wcerr << "Warning: Multiple versions of openssl libraries found.\n";
1364 return {};
1365 }
1366
1367 QFileInfo libPath{ openSslBinDir + slash + lib.front() };
1368 result.append(libPath.absoluteFilePath());
1369 }
1370
1371 return result;
1372}
1373
1374
1385
1386static QString libraryPath(const QString &libraryLocation, const char *name,
1387 const QString &infix, Platform platform, bool debug)
1388{
1389 QString result = libraryLocation + u'/';
1390 result += QLatin1StringView(name);
1391 result += infix;
1392 if (debug && platformHasDebugSuffix(platform))
1393 result += u'd';
1394 result += sharedLibrarySuffix();
1395 return result;
1396}
1397
1398static QString vcDebugRedistDir() { return QStringLiteral("Debug_NonRedist"); }
1399
1401{
1402 const char vcDirVar[] = "VCINSTALLDIR";
1403 const QChar slash(u'/');
1404 QString vcRedistDirName = QDir::cleanPath(QFile::decodeName(qgetenv(vcDirVar)));
1405 if (vcRedistDirName.isEmpty()) {
1406 std::wcerr << "Warning: Cannot find Visual Studio installation directory, " << vcDirVar
1407 << " is not set.\n";
1408 return QString();
1409 }
1410 if (!vcRedistDirName.endsWith(slash))
1411 vcRedistDirName.append(slash);
1412 vcRedistDirName.append(QStringLiteral("redist/MSVC"));
1413 if (!QFileInfo(vcRedistDirName).isDir()) {
1414 std::wcerr << "Warning: Cannot find Visual Studio redist directory, "
1415 << QDir::toNativeSeparators(vcRedistDirName).toStdWString() << ".\n";
1416 return QString();
1417 }
1418 // Look in reverse order for folder containing the debug redist folder
1419 const QFileInfoList subDirs =
1420 QDir(vcRedistDirName)
1421 .entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed);
1422 for (const QFileInfo &f : subDirs) {
1423 QString path = f.absoluteFilePath();
1424 if (QFileInfo(path + slash + vcDebugRedistDir()).isDir())
1425 return path;
1426 path += QStringLiteral("/onecore");
1427 if (QFileInfo(path + slash + vcDebugRedistDir()).isDir())
1428 return path;
1429 }
1430 std::wcerr << "Warning: Cannot find Visual Studio redist directory under "
1431 << QDir::toNativeSeparators(vcRedistDirName).toStdWString() << ".\n";
1432 return QString();
1433}
1434
1435static bool isSystemLibrary(const QString &libraryPath)
1436{
1437 static const QString systemRootEnv = qEnvironmentVariable("SystemRoot");
1438 static bool systemRootEmptyEmitted = false;
1439 if (systemRootEnv.isEmpty()) {
1440 if (!systemRootEmptyEmitted) {
1441 std::wcerr << "Warning: Cannot detect system root.\n";
1442 systemRootEmptyEmitted = true;
1443 }
1444 return false;
1445 }
1446 static QString systemRoot;
1447 if (systemRoot.isEmpty()) {
1448 systemRoot = QDir(QDir::fromNativeSeparators(systemRootEnv)).canonicalPath();
1449 if (!systemRoot.endsWith(u'/'))
1450 systemRoot += u'/';
1451 }
1452
1453 return libraryPath.startsWith(systemRoot, Qt::CaseInsensitive);
1454}
1455
1456// QDir name filters matching the MinGW runtime libraries. The list is empty for platforms that
1457// have no MinGW runtime; callers must check for that, because an empty name filter list makes
1458// QDir list every file.
1459static QStringList minGWRuntimeFilters(Platform platform)
1460{
1461 QStringList stems;
1462 switch (platform) {
1463 case WindowsDesktopMinGW:
1464 stems = { "*gcc_"_L1, "*stdc++"_L1, "*winpthread"_L1 };
1465 break;
1466 case WindowsDesktopClangMinGW:
1467 stems = { "*unwind"_L1, "*c++"_L1 };
1468 break;
1469 default:
1470 return {};
1471 }
1472
1473 const QString suffix = u'*' + sharedLibrarySuffix();
1474 QStringList result;
1475 result.reserve(stems.size());
1476 for (const QString &stem : std::as_const(stems))
1477 result.append(stem + suffix);
1478 return result;
1479}
1480
1481static QStringList findMinGWRuntimePaths(const QString &qtBinDir, Platform platform)
1482{
1483 //MinGW: Add runtime libraries. Check first for the Qt binary directory, and default to path if nothing is found.
1484 const QStringList filters = minGWRuntimeFilters(platform);
1485 if (filters.isEmpty())
1486 return {};
1487
1488 QStringList result;
1489 const bool isClang = platform == WindowsDesktopClangMinGW;
1490 QFileInfoList dlls = QDir(qtBinDir).entryInfoList(filters, QDir::Files);
1491 if (dlls.isEmpty()) {
1492 std::wcerr << "Warning: Runtime libraries not found in Qt binary folder, defaulting to looking in path\n";
1493 const QString binaryPath = isClang ? findInPath("clang++.exe"_L1) : findInPath("g++.exe"_L1);
1494 if (binaryPath.isEmpty()) {
1495 std::wcerr << "Warning: Cannot find " << (isClang ? "Clang" : "GCC") << " installation directory, " << (isClang ? "clang++" : "g++") << ".exe must be in the path\n";
1496 return {};
1497 }
1498 const QString binaryFolder = QFileInfo(binaryPath).absolutePath();
1499 dlls = QDir(binaryFolder).entryInfoList(filters, QDir::Files);
1500 }
1501
1502 for (const QFileInfo &dllFi : dlls)
1503 result.append(dllFi.absoluteFilePath());
1504
1505 return result;
1506}
1507
1508static QStringList compilerRunTimeLibs(const QString &qtBinDir, Platform platform, bool isDebug, unsigned short machineArch)
1509{
1510 QStringList result;
1511 switch (platform) {
1514 result.append(findMinGWRuntimePaths(qtBinDir, platform));
1515 break;
1516#ifdef Q_OS_WIN
1517 case WindowsDesktopMsvcIntel:
1518 case WindowsDesktopMsvcArm: { // MSVC/Desktop: Add redistributable packages.
1519 QString vcRedistDirName = vcRedistDir();
1520 if (vcRedistDirName.isEmpty())
1521 break;
1522 QStringList redistFiles;
1523 QDir vcRedistDir(vcRedistDirName);
1524 const QString machineArchString = getArchString(machineArch);
1525 if (isDebug) {
1526 // Append DLLs from Debug_NonRedist\x??\Microsoft.VC<version>.DebugCRT.
1527 if (vcRedistDir.cd(vcDebugRedistDir()) && vcRedistDir.cd(machineArchString)) {
1528 const QStringList names = vcRedistDir.entryList(QStringList(QStringLiteral("Microsoft.VC*.DebugCRT")), QDir::Dirs);
1529 if (!names.isEmpty() && vcRedistDir.cd(names.first())) {
1530 const QFileInfoList &dlls = vcRedistDir.entryInfoList(QStringList("*.dll"_L1));
1531 for (const QFileInfo &dll : dlls)
1532 redistFiles.append(dll.absoluteFilePath());
1533 }
1534 }
1535 } else { // release: Bundle vcredist<>.exe
1536 QString releaseRedistDir = vcRedistDirName;
1537 const QStringList countryCodes = vcRedistDir.entryList(QStringList(QStringLiteral("[0-9]*")), QDir::Dirs);
1538 if (!countryCodes.isEmpty()) // Pre MSVC2017
1539 releaseRedistDir += u'/' + countryCodes.constFirst();
1540 QFileInfo fi(releaseRedistDir + "/vc_redist."_L1
1541 + machineArchString + ".exe"_L1);
1542 if (!fi.isFile()) { // Pre MSVC2017/15.5
1543 fi.setFile(releaseRedistDir + "/vcredist_"_L1
1544 + machineArchString + ".exe"_L1);
1545 }
1546 if (fi.isFile())
1547 redistFiles.append(fi.absoluteFilePath());
1548 }
1549 if (redistFiles.isEmpty()) {
1550 std::wcerr << "Warning: Cannot find Visual Studio " << (isDebug ? "debug" : "release")
1551 << " redistributable files in " << QDir::toNativeSeparators(vcRedistDirName).toStdWString() << ".\n";
1552 break;
1553 }
1554 result.append(redistFiles);
1555 }
1556 break;
1557#endif // Q_OS_WIN
1558 default:
1559 break;
1560 }
1561 return result;
1562}
1563
1564// File names of the compiler runtime libraries that sit in the Qt binary directory, which is
1565// where the MinGW packages keep them. compilerRunTimeLibs() deploys those, honoring
1566// --compiler-runtime, so they must stay out of the third-party library scan.
1567static QStringList compilerRunTimeLibsInQtBinDir(const QString &qtBinDir, Platform platform)
1568{
1569 const QStringList filters = minGWRuntimeFilters(platform);
1570 if (filters.isEmpty())
1571 return {};
1572 return QDir(qtBinDir).entryList(filters, QDir::Files);
1573}
1574
1575static inline int qtVersion(const QMap<QString, QString> &qtpathsVariables)
1576{
1577 const QString versionString = qtpathsVariables.value(QStringLiteral("QT_VERSION"));
1578 const QChar dot = u'.';
1579 const int majorVersion = versionString.section(dot, 0, 0).toInt();
1580 const int minorVersion = versionString.section(dot, 1, 1).toInt();
1581 const int patchVersion = versionString.section(dot, 2, 2).toInt();
1582 return (majorVersion << 16) | (minorVersion << 8) | patchVersion;
1583}
1584
1585// Deploy a library along with its .pdb debug info file (MSVC) should it exist.
1586static bool updateLibrary(const QString &sourceFileName, const QString &targetDirectory,
1587 const Options &options, QString *errorMessage)
1588{
1589 if (!updateFile(sourceFileName, targetDirectory, options.updateFileFlags, options.json, errorMessage)) {
1590 if (options.ignoreLibraryErrors) {
1591 std::wcerr << "Warning: Could not update " << sourceFileName << " :" << *errorMessage << '\n';
1592 errorMessage->clear();
1593 return true;
1594 }
1595 return false;
1596 }
1597
1598 if (options.deployPdb) {
1599 const QFileInfo pdb(pdbFileName(sourceFileName));
1600 if (pdb.isFile())
1601 return updateFile(pdb.absoluteFilePath(), targetDirectory, options.updateFileFlags, nullptr, errorMessage);
1602 }
1603 return true;
1604}
1605
1606// Find out the ICU version to add the data library icudtXX.dll, which does not
1607// show as a dependency.
1608static QString getIcuVersion(const QString &libName)
1609{
1610 QString version;
1611 std::copy_if(libName.cbegin(), libName.cend(), std::back_inserter(version),
1612 [](QChar c) { return c.isDigit(); });
1613 return version;
1614}
1615
1616static DeployResult deploy(const Options &options, const QMap<QString, QString> &qtpathsVariables,
1617 const PluginInformation &pluginInfo, QString *errorMessage)
1618{
1619 DeployResult result;
1620
1621 const QChar slash = u'/';
1622
1623 const QString qtBinDir = qtpathsVariables.value(QStringLiteral("QT_INSTALL_BINS"));
1624 const QString libraryLocation = qtBinDir;
1625 const QString infix = qtpathsVariables.value(QLatin1StringView(qmakeInfixKey));
1626 const int version = qtVersion(qtpathsVariables);
1627 Q_UNUSED(version);
1628
1629 if (optVerboseLevel > 1)
1630 std::wcout << "Qt binaries in " << QDir::toNativeSeparators(qtBinDir) << '\n';
1631
1632 QStringList dependentQtLibs;
1633 QStringList dependentNonQtLibs;
1634 PeHeaderInfoStruct peHeaderInfo;
1635
1636 if (!readPeExecutableInfo(options.binaries.first(), errorMessage, &peHeaderInfo))
1637 return result;
1638 if (!findDependentQtLibraries(libraryLocation, options.binaries.first(), options.platform,
1639 errorMessage, &dependentQtLibs, &dependentNonQtLibs)) {
1640 return result;
1641 }
1642 for (int b = 1; b < options.binaries.size(); ++b) {
1643 if (!findDependentQtLibraries(libraryLocation, options.binaries.at(b), options.platform,
1644 errorMessage, &dependentQtLibs, &dependentNonQtLibs)) {
1645 return result;
1646 }
1647 }
1648
1649 const QFileInfo fi(options.binaries.first());
1650 const QString canonicalBinPath = fi.canonicalPath();
1651 // Also check Qt dependencies of "local non Qt dependencies" (dlls located in the same folder)
1652 // Index based loop as container might be changed which invalidates iterators
1653 for (qsizetype i = 0; i < dependentNonQtLibs.size(); ++i) {
1654 const QString nonQtLib = dependentNonQtLibs.at(i);
1655 const QString path = canonicalBinPath + u'/' + nonQtLib;
1656 if (!QFileInfo::exists(path))
1657 continue;
1658
1659 if (optVerboseLevel)
1660 std::wcout << "Adding local dependency" << path << '\n';
1661
1662 if (!findDependentQtLibraries(libraryLocation, path, options.platform,
1663 errorMessage, &dependentQtLibs, &dependentNonQtLibs)) {
1664 return result;
1665 }
1666 }
1667
1668 DebugMatchMode debugMatchMode = MatchDebugOrRelease;
1669 result.isDebug = false;
1670 switch (options.debugDetection) {
1672 // Debug detection is only relevant for Msvc/ClangMsvc which have distinct
1673 // runtimes and binaries. For anything else, use MatchDebugOrRelease
1674 // since also debug cannot be reliably detect for MinGW.
1675 if (options.platform.testFlag(Msvc) || options.platform.testFlag(ClangMsvc)) {
1676 result.isDebug = peHeaderInfo.isDebug;
1677 debugMatchMode = result.isDebug ? MatchDebug : MatchRelease;
1678 }
1679 break;
1681 result.isDebug = true;
1682 debugMatchMode = MatchDebug;
1683 break;
1685 debugMatchMode = MatchRelease;
1686 break;
1687 }
1688
1689 // Determine application type, check Quick2 is used by looking at the
1690 // direct dependencies (do not be fooled by QtWebKit depending on it).
1691 for (int m = 0; m < dependentQtLibs.size(); ++m) {
1692 const qint64 module = qtModule(dependentQtLibs.at(m), infix);
1693 if (module >= 0)
1694 result.directlyUsedQtLibraries[module] = 1;
1695 }
1696
1697 const bool usesQml = result.directlyUsedQtLibraries.test(QtQmlModuleId);
1698 const bool usesQuick = result.directlyUsedQtLibraries.test(QtQuickModuleId);
1699 const bool uses3DQuick = result.directlyUsedQtLibraries.test(Qt3DQuickModuleId);
1700 const bool usesQml2 = !(options.disabledLibraries.test(QtQmlModuleId))
1701 && (usesQml || usesQuick || uses3DQuick || (options.additionalLibraries.test(QtQmlModuleId)));
1702
1703 if (optVerboseLevel) {
1704 std::wcout << QDir::toNativeSeparators(options.binaries.first()) << ' '
1705 << peHeaderInfo.wordSize << " bit, " << (result.isDebug ? "debug" : "release")
1706 << " executable";
1707 if (usesQml2)
1708 std::wcout << " [QML]";
1709 std::wcout << '\n';
1710 }
1711
1712 if (dependentQtLibs.isEmpty()) {
1713 *errorMessage = QDir::toNativeSeparators(options.binaries.first()) + QStringLiteral(" does not seem to be a Qt executable.");
1714 return result;
1715 }
1716
1717 // Scan Quick2 imports
1718 QmlImportScanResult qmlScanResult;
1719 if (options.quickImports && usesQml2) {
1720 // Custom list of import paths provided by user
1721 QStringList qmlImportPaths = options.qmlImportPaths;
1722 // Qt's own QML modules
1723 qmlImportPaths << qtpathsVariables.value(QStringLiteral("QT_INSTALL_QML"));
1724 QStringList qmlDirectories = options.qmlDirectories;
1725 if (qmlDirectories.isEmpty()) {
1726 const QString qmlDirectory = findQmlDirectory(options.platform, options.directory);
1727 if (!qmlDirectory.isEmpty())
1728 qmlDirectories.append(qmlDirectory);
1729 }
1730 for (const QString &qmlDirectory : std::as_const(qmlDirectories)) {
1731 if (optVerboseLevel >= 1)
1732 std::wcout << "Scanning " << QDir::toNativeSeparators(qmlDirectory) << ":\n";
1733 const QmlImportScanResult scanResult =
1734 runQmlImportScanner(qmlDirectory, qmlImportPaths,
1735 result.directlyUsedQtLibraries.test(QtWidgetsModuleId),
1736 options.platform, debugMatchMode, errorMessage,
1737 options.qmlImportTimeout);
1738 if (!scanResult.ok)
1739 return result;
1740 qmlScanResult.append(scanResult);
1741 // Additional dependencies of QML plugins.
1742 for (const QString &plugin : std::as_const(qmlScanResult.plugins)) {
1743 if (!findDependentQtLibraries(libraryLocation, plugin, options.platform,
1744 errorMessage, &dependentQtLibs, nullptr)) {
1745 return result;
1746 }
1747 }
1748 if (optVerboseLevel >= 1) {
1749 std::wcout << "QML imports:\n";
1750 for (const QmlImportScanResult::Module &mod : std::as_const(qmlScanResult.modules)) {
1751 std::wcout << " '" << mod.name << "' "
1752 << QDir::toNativeSeparators(mod.sourcePath) << '\n';
1753 }
1754 if (optVerboseLevel >= 2) {
1755 std::wcout << "QML plugins:\n";
1756 for (const QString &p : std::as_const(qmlScanResult.plugins))
1757 std::wcout << " " << QDir::toNativeSeparators(p) << '\n';
1758 }
1759 }
1760 }
1761 }
1762
1763 QString platformPlugin;
1764 // Sort apart Qt 5 libraries in the ones that are represented by the
1765 // QtModule enumeration (and thus controlled by flags) and others.
1766 QStringList deployedQtLibraries;
1767 for (int i = 0 ; i < dependentQtLibs.size(); ++i) {
1768 const qint64 module = qtModule(dependentQtLibs.at(i), infix);
1769 if (module >= 0)
1770 result.usedQtLibraries[module] = 1;
1771 else
1772 deployedQtLibraries.push_back(dependentQtLibs.at(i)); // Not represented by flag.
1773 }
1774 result.deployedQtLibraries = (result.usedQtLibraries | options.additionalLibraries) & ~options.disabledLibraries;
1775
1776 ModuleBitset disabled = options.disabledLibraries;
1777 if (!usesQml2) {
1778 disabled[QtQmlModuleId] = 1;
1779 disabled[QtQuickModuleId] = 1;
1780 }
1781
1782 // Some Windows-specific checks: Qt5Core depends on ICU when configured with "-icu". Other than
1783 // that, Qt5WebKit has a hard dependency on ICU.
1784 if (options.platform.testFlag(WindowsBased)) {
1785 const QStringList qtLibs = dependentQtLibs.filter(QStringLiteral("Qt6Core"), Qt::CaseInsensitive)
1786 + dependentQtLibs.filter(QStringLiteral("Qt5WebKit"), Qt::CaseInsensitive);
1787 for (const QString &qtLib : qtLibs) {
1788 QStringList icuLibs = findDependentLibraries(qtLib, errorMessage).filter(QStringLiteral("ICU"), Qt::CaseInsensitive);
1789 if (!icuLibs.isEmpty()) {
1790 // Find out the ICU version to add the data library icudtXX.dll, which does not show
1791 // as a dependency.
1792 const QString icuVersion = getIcuVersion(icuLibs.constFirst());
1793 if (!icuVersion.isEmpty()) {
1794 if (optVerboseLevel > 1)
1795 std::wcout << "Adding ICU version " << icuVersion << '\n';
1796 QString icuLib = QStringLiteral("icudt") + icuVersion
1797 + QLatin1StringView(windowsSharedLibrarySuffix);
1798 // Some packages contain debug dlls of ICU libraries even though it's a C
1799 // library and the official packages do not differentiate (QTBUG-87677)
1800 if (result.isDebug) {
1801 const QString icuLibCandidate = QStringLiteral("icudtd") + icuVersion
1802 + QLatin1StringView(windowsSharedLibrarySuffix);
1803 if (!findInPath(icuLibCandidate).isEmpty()) {
1804 icuLib = icuLibCandidate;
1805 }
1806 }
1807 icuLibs.push_back(icuLib);
1808 }
1809 for (const QString &icuLib : std::as_const(icuLibs)) {
1810 const QString icuPath = findInPath(icuLib);
1811 if (icuPath.isEmpty()) {
1812 *errorMessage = QStringLiteral("Unable to locate ICU library ") + icuLib;
1813 return result;
1814 }
1815 deployedQtLibraries.push_back(icuPath);
1816 } // for each icuLib
1817 break;
1818 } // !icuLibs.isEmpty()
1819 } // Qt6Core/Qt6WebKit
1820 } // Windows
1821
1822 QStringList openSslLibs;
1823 if (!options.openSslRootDirectory.isEmpty()) {
1824 openSslLibs = findOpenSslLibraries(options.openSslRootDirectory, options.platform);
1825 if (openSslLibs.isEmpty()) {
1826 *errorMessage = QStringLiteral("Unable to find openSSL libraries in ")
1827 + options.openSslRootDirectory;
1828 return result;
1829 }
1830
1831 deployedQtLibraries.append(openSslLibs);
1832 }
1833 const bool deployOpenSslPlugin = options.forceOpenSslPlugin || !openSslLibs.isEmpty();
1834
1835 const QStringList plugins = findQtPlugins(
1836 &result.deployedQtLibraries,
1837 // For non-QML applications, disable QML to prevent it from being pulled in by the
1838 // qtaccessiblequick plugin.
1839 disabled, pluginInfo,
1840 options.pluginSelections, qtpathsVariables.value(QStringLiteral("QT_INSTALL_PLUGINS")),
1841 libraryLocation, infix, debugMatchMode, options.platform, &platformPlugin,
1842 options.deployInsightTrackerPlugin, deployOpenSslPlugin);
1843
1844 // Apply options flags and re-add library names.
1845 QString qtGuiLibrary;
1846 for (const auto &qtModule : qtModuleEntries) {
1847 if (result.deployedQtLibraries.test(qtModule.id)) {
1848 const QString library = libraryPath(libraryLocation, qtModule.name.toUtf8(), infix,
1849 options.platform, result.isDebug);
1850 deployedQtLibraries.append(library);
1851 if (qtModule.id == QtGuiModuleId)
1852 qtGuiLibrary = library;
1853 }
1854 }
1855
1856 if (optVerboseLevel >= 1) {
1857 std::wcout << "Direct dependencies: " << formatQtModules(result.directlyUsedQtLibraries).constData()
1858 << "\nAll dependencies : " << formatQtModules(result.usedQtLibraries).constData()
1859 << "\nTo be deployed : " << formatQtModules(result.deployedQtLibraries).constData() << '\n';
1860 }
1861
1862 if (optVerboseLevel > 1)
1863 std::wcout << "Plugins: " << plugins.join(u',') << '\n';
1864
1865 if (result.deployedQtLibraries.test(QtGuiModuleId) && platformPlugin.isEmpty()) {
1866 *errorMessage =QStringLiteral("Unable to find the platform plugin.");
1867 return result;
1868 }
1869
1870 if (options.platform.testFlag(WindowsBased) && !qtGuiLibrary.isEmpty()) {
1871 const QStringList guiLibraries = findDependentLibraries(qtGuiLibrary, errorMessage);
1872 const bool dependsOnOpenGl = !guiLibraries.filter(QStringLiteral("opengl32"), Qt::CaseInsensitive).isEmpty();
1873 if (options.softwareRasterizer && !dependsOnOpenGl) {
1874 const QFileInfo softwareRasterizer(qtBinDir + slash + QStringLiteral("opengl32sw") + QLatin1StringView(windowsSharedLibrarySuffix));
1875 if (softwareRasterizer.isFile())
1876 deployedQtLibraries.append(softwareRasterizer.absoluteFilePath());
1877 }
1878 if (options.systemD3dCompiler && peHeaderInfo.machineArch != IMAGE_FILE_MACHINE_ARM64) {
1879 const QString d3dCompiler = findD3dCompiler(options.platform, qtBinDir,
1880 peHeaderInfo.wordSize);
1881 if (d3dCompiler.isEmpty()) {
1882 std::wcerr << "Warning: Cannot find any version of the d3dcompiler DLL.\n";
1883 } else {
1884 deployedQtLibraries.push_back(d3dCompiler);
1885 }
1886 }
1887 if (options.systemDxc) {
1888 const QStringList dxcLibs = findDxc(options.platform, qtBinDir,
1889 peHeaderInfo.wordSize);
1890 if (!dxcLibs.isEmpty()) {
1891 deployedQtLibraries.append(dxcLibs);
1892 } else {
1893 std::wcerr << "Warning: Cannot find any version of the dxcompiler.dll and dxil.dll."
1894 << " This is not a problem unless Direct3D 12 and certain features are used by the application.\n";
1895 }
1896 }
1897 } // Windows
1898
1899 // Add FFmpeg if we deploy the FFmpeg backend
1900 if (options.ffmpeg
1901 && !plugins.filter(QStringLiteral("ffmpegmediaplugin"), Qt::CaseInsensitive).empty()) {
1902 deployedQtLibraries.append(findFFmpegLibs(qtBinDir, options.platform));
1903 }
1904
1905 // Add the non-Qt libraries from the Qt binary directory that the deployed binaries link
1906 // against. Qt built against system libraries needs those next to the Qt libraries.
1907 if (options.thirdPartyLibraries) {
1908 QStringList scanned = options.binaries + deployedQtLibraries;
1909 if (options.plugins)
1910 scanned += plugins;
1911 if (options.quickImports)
1912 scanned += qmlScanResult.plugins;
1913
1914 // Libraries that are deployed already, or that another deployment step owns.
1915 QSet<QString> excludedFileNames;
1916 for (const QString &lib : std::as_const(deployedQtLibraries))
1917 excludedFileNames.insert(QFileInfo(lib).fileName().toLower());
1918 for (const QString &lib : compilerRunTimeLibsInQtBinDir(qtBinDir, options.platform))
1919 excludedFileNames.insert(lib.toLower());
1920
1921 QStringList thirdPartyLibraries;
1922 if (!findThirdPartyDependencies(qtBinDir, scanned, !options.ffmpeg, excludedFileNames,
1923 &thirdPartyLibraries, errorMessage)) {
1924 return result;
1925 }
1926 deployedQtLibraries += thirdPartyLibraries;
1927 }
1928
1929 // Update libraries
1930 if (options.libraries) {
1931 const QString targetPath = options.libraryDirectory.isEmpty() ?
1932 options.directory : options.libraryDirectory;
1933 QStringList libraries = deployedQtLibraries;
1934 if (options.compilerRunTime) {
1935 libraries.append(compilerRunTimeLibs(qtBinDir, options.platform, result.isDebug,
1936 peHeaderInfo.machineArch));
1937 }
1938 for (const QString &qtLib : std::as_const(libraries)) {
1939 if (isSystemLibrary(qtLib)) {
1940 if (optVerboseLevel)
1941 std::wcout << "Skipping system library " << qtLib << "\n";
1942 continue;
1943 }
1944 if (!updateLibrary(qtLib, targetPath, options, errorMessage))
1945 return result;
1946 }
1947
1948#if !QT_CONFIG(relocatable)
1949 if (options.patchQt && !options.dryRun) {
1950 const QString qt6CoreName = QFileInfo(libraryPath(libraryLocation, "Qt6Core", infix,
1951 options.platform, result.isDebug)).fileName();
1952 if (!patchQtCore(targetPath + u'/' + qt6CoreName, errorMessage)) {
1953 std::wcerr << "Warning: " << *errorMessage << '\n';
1954 errorMessage->clear();
1955 }
1956 }
1957#endif // QT_CONFIG(relocatable)
1958 } // optLibraries
1959
1960 // Update plugins
1961 if (options.plugins) {
1962 const QString targetPath = options.pluginDirectory.isEmpty() ?
1963 options.directory : options.pluginDirectory;
1964 QDir dir(targetPath);
1965 if (!dir.exists() && !dir.mkpath(QStringLiteral("."))) {
1966 *errorMessage = "Cannot create "_L1 +
1967 QDir::toNativeSeparators(dir.absolutePath()) + u'.';
1968 return result;
1969 }
1970 for (const QString &plugin : plugins) {
1971 const QString targetDirName = plugin.section(slash, -2, -2);
1972 const QString targetPath = dir.absoluteFilePath(targetDirName);
1973 if (!dir.exists(targetDirName)) {
1974 if (optVerboseLevel)
1975 std::wcout << "Creating directory " << targetPath << ".\n";
1976 if (!(options.updateFileFlags & SkipUpdateFile) && !dir.mkdir(targetDirName)) {
1977 *errorMessage = QStringLiteral("Cannot create ") + targetDirName + u'.';
1978 return result;
1979 }
1980 }
1981 if (!updateLibrary(plugin, targetPath, options, errorMessage))
1982 return result;
1983 }
1984 } // optPlugins
1985
1986 // Update Quick imports
1987 // Do not be fooled by QtWebKit.dll depending on Quick into always installing Quick imports
1988 // for WebKit1-applications. Check direct dependency only.
1989 if (options.quickImports && usesQml2) {
1990 const QString targetPath = options.qmlDirectory.isEmpty()
1991 ? options.directory + QStringLiteral("/qml")
1992 : options.qmlDirectory;
1993 if (!createDirectory(targetPath, errorMessage, options.dryRun))
1994 return result;
1995 for (const QmlImportScanResult::Module &module : std::as_const(qmlScanResult.modules)) {
1996 const QString installPath = module.installPath(targetPath);
1997 if (optVerboseLevel > 1)
1998 std::wcout << "Installing: '" << module.name
1999 << "' from " << module.sourcePath << " to "
2000 << QDir::toNativeSeparators(installPath) << '\n';
2001 if (installPath != targetPath && !createDirectory(installPath, errorMessage, options.dryRun))
2002 return result;
2003 unsigned updateFileFlags = options.updateFileFlags
2004 | SkipQmlDesignerSpecificsDirectories;
2005 unsigned qmlDirectoryFileFlags = 0;
2006 if (options.deployPdb)
2007 qmlDirectoryFileFlags |= QmlDirectoryFileEntryFunction::DeployPdb;
2008 if (!updateFile(module.sourcePath, QmlDirectoryFileEntryFunction(module.sourcePath,
2009 options.platform,
2010 debugMatchMode,
2011 qmlDirectoryFileFlags),
2012 installPath, updateFileFlags, options.json, errorMessage)) {
2013 return result;
2014 }
2015 }
2016 } // optQuickImports
2017
2018 if (options.translations) {
2019 const QString &catalogsError = qtModuleEntries.translationCatalogsError();
2020 if (catalogsError.isEmpty()) {
2021 if (!createDirectory(options.translationsDirectory, errorMessage, options.dryRun))
2022 return result;
2023 if (!deployTranslations(qtpathsVariables.value(QStringLiteral("QT_INSTALL_TRANSLATIONS")),
2024 result.deployedQtLibraries, options.translationsDirectory,
2025 options, errorMessage)) {
2026 return result;
2027 }
2028 } else {
2029 std::wcerr << "Warning: Translations will not be deployed due to the following error."
2030 << std::endl << catalogsError << std::endl;
2031 }
2032 }
2033
2034 result.success = true;
2035 return result;
2036}
2037
2038static bool deployWebProcess(const QMap<QString, QString> &qtpathsVariables, const char *binaryName,
2039 const PluginInformation &pluginInfo, const Options &sourceOptions,
2040 QString *errorMessage)
2041{
2042 // Copy the web process and its dependencies
2043 const QString webProcess = webProcessBinary(binaryName, sourceOptions.platform);
2044 const QString webProcessSource = qtpathsVariables.value(QStringLiteral("QT_INSTALL_LIBEXECS"))
2045 + u'/' + webProcess;
2046 const QString webProcessTargetDir = sourceOptions.platform & WindowsBased
2047 && !sourceOptions.libraryDirectory.isEmpty() ?
2048 sourceOptions.libraryDirectory :
2049 sourceOptions.directory;
2050 if (!updateFile(webProcessSource, webProcessTargetDir, sourceOptions.updateFileFlags, sourceOptions.json, errorMessage))
2051 return false;
2052 Options options(sourceOptions);
2053 options.binaries.append(webProcessTargetDir + u'/' + webProcess);
2054 options.quickImports = false;
2055 options.translations = false;
2056 return deploy(options, qtpathsVariables, pluginInfo, errorMessage);
2057}
2058
2059static bool deployWebEngineCore(const QMap<QString, QString> &qtpathsVariables,
2060 const PluginInformation &pluginInfo, const Options &options,
2061 bool isDebug, QString *errorMessage)
2062{
2063 static const char *installDataFilesRelease[] = {
2064 "icudtl.dat", "qtwebengine_devtools_resources.pak", "qtwebengine_resources.pak",
2065 "qtwebengine_resources_100p.pak", "qtwebengine_resources_200p.pak"
2066 };
2067 static const char *installDataFilesDebug[] = {
2068 "icudtl.dat", "qtwebengine_devtools_resources.debug.pak", "qtwebengine_resources.debug.pak",
2069 "qtwebengine_resources_100p.debug.pak", "qtwebengine_resources_200p.debug.pak"
2070 };
2071 static const auto &installDataFiles = isDebug ? installDataFilesDebug : installDataFilesRelease;
2072 static const auto installV8SnapshotFile =
2073 isDebug ? "v8_context_snapshot.debug.bin" : "v8_context_snapshot.bin";
2074
2075 QByteArray webEngineProcessName(webEngineProcessC);
2076 if (isDebug && platformHasDebugSuffix(options.platform))
2077 webEngineProcessName.append('d');
2078 if (optVerboseLevel)
2079 std::wcout << "Deploying: " << webEngineProcessName.constData() << "...\n";
2080 if (!deployWebProcess(qtpathsVariables, webEngineProcessName, pluginInfo, options, errorMessage))
2081 return false;
2082 const QString resourcesSubDir = QStringLiteral("/resources");
2083 const QString resourcesSourceDir = qtpathsVariables.value(QStringLiteral("QT_INSTALL_DATA"))
2084 + resourcesSubDir + u'/';
2085 const QString resourcesTargetDir(options.directory + resourcesSubDir);
2086 if (!createDirectory(resourcesTargetDir, errorMessage, options.dryRun))
2087 return false;
2088 for (auto installDataFile : installDataFiles) {
2089 if (!updateFile(resourcesSourceDir + QLatin1StringView(installDataFile),
2090 resourcesTargetDir, options.updateFileFlags, options.json, errorMessage)) {
2091 return false;
2092 }
2093 }
2094 // snapshot file is optional feature in qtwebengine, so it might be missing
2095 updateFile(resourcesSourceDir + QLatin1StringView(installV8SnapshotFile), resourcesTargetDir,
2096 options.updateFileFlags, options.json, errorMessage);
2097 errorMessage->clear();
2098 const QFileInfo translations(qtpathsVariables.value(QStringLiteral("QT_INSTALL_TRANSLATIONS"))
2099 + QStringLiteral("/qtwebengine_locales"));
2100 if (!translations.isDir()) {
2101 std::wcerr << "Warning: Cannot find the translation files of the QtWebEngine module at "
2102 << QDir::toNativeSeparators(translations.absoluteFilePath()) << ".\n";
2103 return true;
2104 }
2105 if (options.translations) {
2106 // Copy the whole translations directory.
2107 return createDirectory(options.translationsDirectory, errorMessage, options.dryRun)
2108 && updateFile(translations.absoluteFilePath(), options.translationsDirectory,
2109 options.updateFileFlags, options.json, errorMessage);
2110 }
2111 // Translations have been turned off, but QtWebEngine needs at least one.
2112 const QFileInfo enUSpak(translations.filePath() + QStringLiteral("/en-US.pak"));
2113 if (!enUSpak.exists()) {
2114 std::wcerr << "Warning: Cannot find "
2115 << QDir::toNativeSeparators(enUSpak.absoluteFilePath()) << ".\n";
2116 return true;
2117 }
2118 const QString webEngineTranslationsDir = options.translationsDirectory + u'/'
2119 + translations.fileName();
2120 if (!createDirectory(webEngineTranslationsDir, errorMessage, options.dryRun))
2121 return false;
2122 return updateFile(enUSpak.absoluteFilePath(), webEngineTranslationsDir,
2123 options.updateFileFlags, options.json, errorMessage);
2124}
2125
2126QT_END_NAMESPACE
2127
2128QT_USE_NAMESPACE
2129
2130int main(int argc, char **argv)
2131{
2132 QCoreApplication a(argc, argv);
2133 QCoreApplication::setApplicationVersion(QT_VERSION_STR ""_L1);
2134
2135 const QByteArray qtBinPath = QFile::encodeName(QDir::toNativeSeparators(QCoreApplication::applicationDirPath()));
2136 QByteArray path = qgetenv("PATH");
2137 if (!path.contains(qtBinPath)) { // QTBUG-39177, ensure Qt is in the path so that qt.conf is taken into account.
2138 path.prepend(QDir::listSeparator().toLatin1());
2139 path.prepend(qtBinPath);
2140 qputenv("PATH", path);
2141 }
2142
2143 Options options;
2144 QString errorMessage;
2145
2146 // Early parse the --qmake and --qtpaths options, because they are needed to determine the
2147 // options that select/deselect Qt modules.
2148 {
2149 int result = parseEarlyArguments(QCoreApplication::arguments(), &options, &errorMessage);
2150 if (result & CommandLineParseError) {
2151 std::wcerr << "Error: " << errorMessage << "\n";
2152 return 1;
2153 }
2154 }
2155
2156 const QMap<QString, QString> qtpathsVariables =
2157 queryQtPaths(options.qtpathsBinary, &errorMessage);
2158 const QString xSpec = qtpathsVariables.value(QStringLiteral("QMAKE_XSPEC"));
2159 if (qtpathsVariables.isEmpty() || xSpec.isEmpty()
2160 || !qtpathsVariables.contains(QStringLiteral("QT_INSTALL_BINS"))) {
2161 std::wcerr << "Unable to query qtpaths: " << errorMessage << '\n';
2162 return 1;
2163 }
2164
2165 options.platform = platformFromMkSpec(xSpec);
2166 // We are on MSVC and not crosscompiling. We need the host arch
2167 if (options.platform == WindowsDesktopMsvc) {
2168 SYSTEM_INFO si;
2169 GetSystemInfo(&si);
2170 switch (si.wProcessorArchitecture) {
2171 case PROCESSOR_ARCHITECTURE_INTEL:
2172 case PROCESSOR_ARCHITECTURE_IA64:
2173 case PROCESSOR_ARCHITECTURE_AMD64:
2174 options.platform |= IntelBased;
2175 break;
2176 case PROCESSOR_ARCHITECTURE_ARM:
2177 case PROCESSOR_ARCHITECTURE_ARM64:
2178 options.platform |= ArmBased;
2179 break;
2180 default:
2181 options.platform = UnknownPlatform;
2182 }
2183 }
2184 if (options.platform == UnknownPlatform) {
2185 std::wcerr << "Unsupported platform " << xSpec << '\n';
2186 return 1;
2187 }
2188
2189 // Read the Qt module information from the Qt installation directory.
2190 const QString modulesDir
2191 = qtpathsVariables.value(QLatin1String("QT_INSTALL_ARCHDATA"))
2192 + QLatin1String("/modules");
2193 const QString translationsDir
2194 = qtpathsVariables.value(QLatin1String("QT_INSTALL_TRANSLATIONS"));
2195 if (!qtModuleEntries.populate(modulesDir, translationsDir, optVerboseLevel > 1,
2196 &errorMessage)) {
2197 std::wcerr << "Error: " << errorMessage << "\n";
2198 return 1;
2199 }
2201
2202 // Read the Qt plugin types information from the Qt installation directory.
2203 PluginInformation pluginInfo{};
2204 pluginInfo.generateAvailablePlugins(qtpathsVariables, options.platform);
2205
2206 // Parse the full command line.
2207 {
2208 QCommandLineParser parser;
2209 QString errorMessage;
2210 const int result = parseArguments(QCoreApplication::arguments(), &parser, &options, &errorMessage);
2211 if (result & CommandLineParseError)
2212 std::wcerr << errorMessage << "\n\n";
2213 if (result & CommandLineVersionRequested) {
2214 std::fputs(QT_VERSION_STR "\n", stdout);
2215 return 0;
2216 }
2217 if (result & CommandLineParseHelpRequested)
2218 std::fputs(qPrintable(helpText(parser, pluginInfo)), stdout);
2219 if (result & CommandLineParseError)
2220 return 1;
2221 if (result & CommandLineParseHelpRequested)
2222 return 0;
2223 }
2224
2225 // Create directories
2226 if (!createDirectory(options.directory, &errorMessage, options.dryRun)) {
2227 std::wcerr << errorMessage << '\n';
2228 return 1;
2229 }
2230 if (!options.libraryDirectory.isEmpty() && options.libraryDirectory != options.directory
2231 && !createDirectory(options.libraryDirectory, &errorMessage, options.dryRun)) {
2232 std::wcerr << errorMessage << '\n';
2233 return 1;
2234 }
2235
2236 const DeployResult result = deploy(options, qtpathsVariables, pluginInfo, &errorMessage);
2237 if (!result) {
2238 std::wcerr << errorMessage << '\n';
2239 return 1;
2240 }
2241
2242 if (result.deployedQtLibraries.test(QtWebEngineCoreModuleId)) {
2243 if (!deployWebEngineCore(qtpathsVariables, pluginInfo, options, result.isDebug,
2244 &errorMessage)) {
2245 std::wcerr << errorMessage << '\n';
2246 return 1;
2247 }
2248 }
2249
2250 if (options.createAppx && !options.appxCertificatePath.isEmpty()) {
2251 const QFileInfo storeLogo(options.directory + QStringLiteral("/Assets/StoreLogo.png"));
2252 if (!storeLogo.exists()) {
2253 std::wcerr << "Error: Could not open application logo file " << storeLogo.absoluteFilePath() << '\n';
2254 return 1;
2255 }
2256
2257 QFile certFile(options.appxCertificatePath);
2258 if (!certFile.open(QIODevice::ReadOnly)) {
2259 std::wcerr << "Could not open certificate file" << '\n';
2260 return 1;
2261 }
2262
2263 QSslCertificate cert(&certFile, QSsl::Der);
2264 QString publisher = cert.subjectDisplayName();
2265
2266 const QString applicationName = QFileInfo(options.binaries.first()).baseName();
2267 const QString platform = options.platform.testFlag(PlatformFlag::IntelBased) ? QStringLiteral("x64") : QStringLiteral("arm64");
2268 const QString appxFilePath(options.directory + QStringLiteral("/") + QStringLiteral("AppxManifest.xml"));
2269 QFile f(appxFilePath);
2270 if (!f.open(QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text)) {
2271 std::wcerr << "Could not create AppxManifest.xml" << '\n';
2272 return 1;
2273 }
2274
2275 QXmlStreamWriter manifestWriter(&f);
2276 manifestWriter.setAutoFormatting(true);
2277 manifestWriter.writeStartDocument();
2278 manifestWriter.writeStartElement(QStringLiteral("Package"));
2279 manifestWriter.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.microsoft.com/appx/manifest/foundation/windows10"));
2280 manifestWriter.writeAttribute(QStringLiteral("xmlns:uap"), QStringLiteral("http://schemas.microsoft.com/appx/manifest/uap/windows10"));
2281 manifestWriter.writeAttribute(QStringLiteral("xmlns:rescap"), QStringLiteral("http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"));
2282
2283 manifestWriter.writeStartElement(QStringLiteral("Identity"));
2284 manifestWriter.writeAttribute("Name", QUuid::createUuid().toString(QUuid::WithoutBraces));
2285 manifestWriter.writeAttribute("Publisher", QStringLiteral("CN=") + publisher);
2286 manifestWriter.writeAttribute("Version", "1.0.0.0");
2287 manifestWriter.writeAttribute("ProcessorArchitecture", platform);
2288 manifestWriter.writeEndElement();
2289
2290 manifestWriter.writeStartElement("Properties");
2291 manifestWriter.writeStartElement("DisplayName");
2292 manifestWriter.writeCharacters(applicationName);
2293 manifestWriter.writeEndElement();
2294 manifestWriter.writeStartElement("PublisherDisplayName");
2295 manifestWriter.writeCharacters(publisher);
2296 manifestWriter.writeEndElement();
2297 manifestWriter.writeStartElement("Logo");
2298 manifestWriter.writeCharacters("Assets/StoreLogo.png");
2299 manifestWriter.writeEndElement();
2300 manifestWriter.writeEndElement();
2301
2302 manifestWriter.writeStartElement("Dependencies");
2303 manifestWriter.writeStartElement("TargetDeviceFamily");
2304 manifestWriter.writeAttribute("Name", "Windows.Desktop");
2305 manifestWriter.writeAttribute("MinVersion", "10.0.14316.0");
2306 manifestWriter.writeAttribute("MaxVersionTested", "10.0.14316.0");
2307 manifestWriter.writeEndElement();
2308 manifestWriter.writeEndElement();
2309
2310 manifestWriter.writeStartElement("Capabilities");
2311 manifestWriter.writeStartElement("rescap:Capability");
2312 manifestWriter.writeAttribute("Name", "runFullTrust");
2313 manifestWriter.writeEndElement();
2314 manifestWriter.writeEndElement();
2315
2316 manifestWriter.writeStartElement("Resources");
2317 manifestWriter.writeStartElement("Resource");
2318 if (options.languages.isEmpty()) {
2319 QLocale locale = QLocale::system();
2320 manifestWriter.writeAttribute("Language", locale.bcp47Name());
2321 } else {
2322 for (const auto& language : options.languages) {
2323 manifestWriter.writeAttribute("Language", language);
2324 }
2325 }
2326 manifestWriter.writeEndElement();
2327 manifestWriter.writeEndElement();
2328
2329 manifestWriter.writeStartElement("Applications");
2330 for (const auto& binary : options.binaries) {
2331 const QString binaryRelative = binary.split(QStringLiteral("/")).last();
2332 const QString displayName = binaryRelative.split(QStringLiteral(".")).first();
2333 QFile descriptionFile(options.directory + QStringLiteral("/") + QStringLiteral("Assets/Description_") + displayName + QStringLiteral(".txt"));
2334 QString description;
2335 if (!descriptionFile.exists())
2336 std::wcerr << "Warning: No package description was provided " << descriptionFile.fileName() << '\n';
2337 if (descriptionFile.open(QIODevice::ReadOnly | QIODevice::Text))
2338 description = QString::fromUtf8(descriptionFile.readAll());
2339
2340 manifestWriter.writeStartElement("Application");
2341 manifestWriter.writeAttribute("Id", displayName);
2342 manifestWriter.writeAttribute("Executable", binaryRelative);
2343 manifestWriter.writeAttribute("EntryPoint", "Windows.FullTrustApplication");
2344 manifestWriter.writeStartElement("uap:VisualElements");
2345 manifestWriter.writeAttribute("DisplayName", displayName);
2346 manifestWriter.writeAttribute("Description", description);
2347 manifestWriter.writeAttribute("BackgroundColor", "transparent");
2348 manifestWriter.writeAttribute("Square150x150Logo", "Assets/Logo.png");
2349 manifestWriter.writeAttribute("Square44x44Logo", "Assets/SmallLogo.png");
2350 manifestWriter.writeStartElement("uap:DefaultTile");
2351 manifestWriter.writeEndElement();
2352 manifestWriter.writeEndElement();
2353 manifestWriter.writeEndElement();
2354 }
2355 manifestWriter.writeEndElement();
2356 manifestWriter.writeEndElement();
2357 manifestWriter.writeEndDocument();
2358 }
2359
2360 if (options.json) {
2361 if (options.list)
2362 std::fputs(options.json->toList(options.list, options.directory).constData(), stdout);
2363 else
2364 std::fputs(options.json->toJson().constData(), stdout);
2365 delete options.json;
2366 options.json = nullptr;
2367 }
2368
2369 return 0;
2370}
QStringList operator()(const QDir &dir) const
Definition main.cpp:898
DllDirectoryFileEntryFunction(Platform platform, DebugMatchMode debugMatchMode, const QString &prefix=QString())
Definition main.cpp:894
QStringList operator()(const QDir &dir) const
Definition main.cpp:935
QmlDirectoryFileEntryFunction(const QString &moduleSourcePath, Platform platform, DebugMatchMode debugMatchMode, unsigned flags)
Definition main.cpp:929
static bool isSystemLibrary(const QString &libName)
Definition main.cpp:2160
static Platform platformFromMkSpec(const QString &xSpec)
Definition main.cpp:137
static bool isQtModule(const QString &libName)
Definition main.cpp:847
static QString libraryPath(const QString &libraryLocation, const char *name, const QString &infix, Platform platform, bool debug)
Definition main.cpp:1386
static QString lineBreak(QString s)
Definition main.cpp:800
static QString msgFileDoesNotExist(const QString &file)
Definition main.cpp:241
static QStringList compilerRunTimeLibs(const QString &qtBinDir, Platform platform, bool isDebug, unsigned short machineArch)
Definition main.cpp:1508
static bool updateLibrary(const QString &sourceFileName, const QString &targetDirectory, const Options &options, QString *errorMessage)
Definition main.cpp:1586
static bool findDependentQtLibraries(const QString &qtBinDir, const QString &binary, Platform platform, QString *errorMessage, QStringList *qtDependencies, QStringList *nonQtDependencies)
Definition main.cpp:857
static void assignKnownModuleIds()
Definition main.cpp:62
static QCommandLineOption createVerboseOption()
Definition main.cpp:270
ExlusiveOptionValue
Definition main.cpp:155
@ OptionEnabled
Definition main.cpp:157
@ OptionAuto
Definition main.cpp:156
@ OptionDisabled
Definition main.cpp:158
static int parseArguments(const QStringList &arguments, QCommandLineParser *parser, Options *options, QString *errorMessage)
Definition main.cpp:338
static constexpr std::array ffmpegHints
Definition main.cpp:1266
static ExlusiveOptionValue parseExclusiveOptions(const QCommandLineParser *parser, const QCommandLineOption &enableOption, const QCommandLineOption &disableOption)
Definition main.cpp:161
static int qtVersion(const QMap< QString, QString > &qtpathsVariables)
Definition main.cpp:1575
static QStringList translationNameFilters(const ModuleBitset &modules, const QString &prefix)
Definition main.cpp:1196
static QString findBinary(const QString &directory, Platform platform)
Definition main.cpp:226
static DeployResult deploy(const Options &options, const QMap< QString, QString > &qtpathsVariables, const PluginInformation &pluginInfo, QString *errorMessage)
Definition main.cpp:1616
static int parseEarlyArguments(const QStringList &arguments, Options *options, QString *errorMessage)
Definition main.cpp:279
static QCommandLineOption createQMakeOption()
Definition main.cpp:252
static bool deployWebEngineCore(const QMap< QString, QString > &qtpathsVariables, const PluginInformation &pluginInfo, const Options &options, bool isDebug, QString *errorMessage)
Definition main.cpp:2059
static QStringList qmlCacheFileFilters()
Definition main.cpp:915
static QString pdbFileName(QString libraryFileName)
Definition main.cpp:907
CommandLineParseFlag
Definition main.cpp:246
@ CommandLineParseError
Definition main.cpp:247
@ CommandLineVersionRequested
Definition main.cpp:249
@ CommandLineParseHelpRequested
Definition main.cpp:248
static QString moduleNameToOptionName(const QString &moduleName, bool internal)
Definition main.cpp:95
static QString formatQtPlugins(const PluginInformation &pluginInfo)
Definition main.cpp:122
#define DECLARE_KNOWN_MODULE(name)
Definition main.cpp:45
static QCommandLineOption createQtPathsOption()
Definition main.cpp:261
static QStringList minGWRuntimeFilters(Platform platform)
Definition main.cpp:1459
static bool needsPluginType(const QString &subDirName, const PluginInformation &pluginInfo, const PluginSelections &pluginSelections)
Definition main.cpp:1111
static QStringList compilerRunTimeLibsInQtBinDir(const QString &qtBinDir, Platform platform)
Definition main.cpp:1567
static qint64 qtModule(QString module, const QString &infix)
Definition main.cpp:987
static QString vcRedistDir()
Definition main.cpp:1400
static QString webProcessBinary(const char *binaryName, Platform p)
Definition main.cpp:89
static QStringList findFFmpegLibs(const QString &qtBinDir, Platform platform)
Definition main.cpp:1321
static const char webEngineProcessC[]
Definition main.cpp:87
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)
Definition main.cpp:1013
static QStringList findMinGWRuntimePaths(const QString &qtBinDir, Platform platform)
Definition main.cpp:1481
static QString helpText(const QCommandLineParser &p, const PluginInformation &pluginInfo)
Definition main.cpp:812
static bool deployWebProcess(const QMap< QString, QString > &qtpathsVariables, const char *binaryName, const PluginInformation &pluginInfo, const Options &sourceOptions, QString *errorMessage)
Definition main.cpp:2038
static bool findThirdPartyDependencies(const QString &qtBinDir, const QStringList &binaries, bool excludeFFmpeg, QSet< QString > excludedFileNames, QStringList *thirdPartyLibraries, QString *errorMessage)
Definition main.cpp:1280
#define DEFINE_KNOWN_MODULE(name)
Definition main.cpp:59
#define IMAGE_FILE_MACHINE_ARM64
Definition main.cpp:27
static QString getIcuVersion(const QString &libName)
Definition main.cpp:1608
static bool deployTranslations(const QString &sourcePath, const ModuleBitset &usedQtModules, const QString &target, const Options &options, QString *errorMessage)
Definition main.cpp:1209
static bool isFFmpegLibrary(const QString &libraryFileName)
Definition main.cpp:1269
static QtModuleInfoStore qtModuleEntries
Definition main.cpp:43
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)
Definition main.cpp:1122
static QStringList findOpenSslLibraries(const QString &openSslRootDir, Platform platform)
Definition main.cpp:1347
static QString vcDebugRedistDir()
Definition main.cpp:1398
static QByteArray formatQtModules(const ModuleBitset &mask, bool option=false)
Definition main.cpp:107
PlatformFlag
Definition utils.h:26
@ MinGW
Definition utils.h:34
@ WindowsDesktopMsvcArm
Definition utils.h:40
@ ClangMsvc
Definition utils.h:35
@ WindowsBased
Definition utils.h:28
@ WindowsDesktopMsvc
Definition utils.h:38
@ Msvc
Definition utils.h:33
@ WindowsDesktopMinGW
Definition utils.h:41
@ UnknownPlatform
Definition utils.h:44
@ ArmBased
Definition utils.h:31
@ WindowsDesktopClangMinGW
Definition utils.h:43
@ WindowsDesktopClangMsvc
Definition utils.h:42
@ IntelBased
Definition utils.h:30
int optVerboseLevel
Definition utils.cpp:28
const char * qmakeInfixKey
Definition utils.cpp:192
@ SkipUpdateFile
Definition utils.h:208
@ ForceUpdateFile
Definition utils.h:207
static const char windowsSharedLibrarySuffix[]
Definition utils.h:145
DebugMatchMode
Definition utils.h:158
@ MatchRelease
Definition utils.h:160
@ MatchDebug
Definition utils.h:159
@ MatchDebugOrRelease
Definition utils.h:161
int main(int argc, char *argv[])
[ctor_close]
ModuleBitset usedQtLibraries
Definition main.cpp:1382
ModuleBitset directlyUsedQtLibraries
Definition main.cpp:1381
operator bool() const
Definition main.cpp:1377
bool isDebug
Definition main.cpp:1380
ModuleBitset deployedQtLibraries
Definition main.cpp:1383
bool success
Definition main.cpp:1379
QString libraryDirectory
Definition main.cpp:207
bool translations
Definition main.cpp:188
bool thirdPartyLibraries
Definition main.cpp:194
JsonOutput * json
Definition main.cpp:212
DebugDetection debugDetection
Definition main.cpp:214
QStringList binaries
Definition main.cpp:211
bool quickImports
Definition main.cpp:187
bool systemDxc
Definition main.cpp:190
bool ffmpeg
Definition main.cpp:193
bool forceOpenSslPlugin
Definition main.cpp:220
unsigned updateFileFlags
Definition main.cpp:199
int qmlImportTimeout
Definition main.cpp:202
ListOption list
Definition main.cpp:213
QStringList qmlImportPaths
Definition main.cpp:169
QString qtpathsBinary
Definition main.cpp:204
bool deployPdb
Definition main.cpp:215
PluginSelections pluginSelections
Definition main.cpp:195
QStringList qmlDirectories
Definition main.cpp:200
QString translationsDirectory
Definition main.cpp:205
QStringList languages
Definition main.cpp:206
ModuleBitset additionalLibraries
Definition main.cpp:197
QString directory
Definition main.cpp:203
bool patchQt
Definition main.cpp:217
bool systemD3dCompiler
Definition main.cpp:189
Platform platform
Definition main.cpp:196
bool createAppx
Definition main.cpp:221
bool ignoreLibraryErrors
Definition main.cpp:218
QString pluginDirectory
Definition main.cpp:208
QString qmlDirectory
Definition main.cpp:210
bool softwareRasterizer
Definition main.cpp:192
QString openSslRootDirectory
Definition main.cpp:209
ModuleBitset disabledLibraries
Definition main.cpp:198
bool dryRun
Definition main.cpp:216
bool deployInsightTrackerPlugin
Definition main.cpp:219
QString appxCertificatePath
Definition main.cpp:222
bool plugins
Definition main.cpp:185
DebugDetection
Definition main.cpp:179
@ DebugDetectionForceRelease
Definition main.cpp:182
@ DebugDetectionForceDebug
Definition main.cpp:181
@ DebugDetectionAuto
Definition main.cpp:180
bool libraries
Definition main.cpp:186
bool compilerRunTime
Definition main.cpp:191