335static inline int parseArguments(
const QStringList &arguments, QCommandLineParser *parser,
336 Options *options, QString *errorMessage)
338 using CommandLineOptionPtr = QSharedPointer<QCommandLineOption>;
339 using OptionPtrVector = QList<CommandLineOptionPtr>;
341 parser->setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
342 parser->setApplicationDescription(u"Qt Deploy Tool " QT_VERSION_STR
343 "\n\nThe simplest way to use windeployqt is to add the bin directory of your Qt\n"
344 "installation (e.g. <QT_DIR\\bin>) to the PATH variable and then run:\n windeployqt <path-to-app-binary>\n\n"
345 "If your application uses Qt Quick, run:\n windeployqt --qmldir <path-to-app-qml-files> <path-to-app-binary>"_s);
346 const QCommandLineOption helpOption = parser->addHelpOption();
347 const QCommandLineOption versionOption = parser->addVersionOption();
349 QCommandLineOption dirOption(QStringLiteral(
"dir"),
350 QStringLiteral(
"Use directory instead of binary directory."),
351 QStringLiteral(
"directory"));
352 parser->addOption(dirOption);
355 parser->addOption(createQMakeOption());
356 parser->addOption(createQtPathsOption());
358 QCommandLineOption libDirOption(QStringLiteral(
"libdir"),
359 QStringLiteral(
"Copy libraries to path."),
360 QStringLiteral(
"path"));
361 parser->addOption(libDirOption);
363 QCommandLineOption pluginDirOption(QStringLiteral(
"plugindir"),
364 QStringLiteral(
"Copy plugins to path."),
365 QStringLiteral(
"path"));
366 parser->addOption(pluginDirOption);
368 const QCommandLineOption translationDirOption(
370 u"Copy translations to path."_s,
372 parser->addOption(translationDirOption);
374 QCommandLineOption qmlDeployDirOption(QStringLiteral(
"qml-deploy-dir"),
375 QStringLiteral(
"Copy qml files to path."),
376 QStringLiteral(
"path"));
377 parser->addOption(qmlDeployDirOption);
379 QCommandLineOption debugOption(QStringLiteral(
"debug"),
380 QStringLiteral(
"Assume debug binaries."));
381 parser->addOption(debugOption);
382 QCommandLineOption releaseOption(QStringLiteral(
"release"),
383 QStringLiteral(
"Assume release binaries."));
384 parser->addOption(releaseOption);
385 QCommandLineOption releaseWithDebugInfoOption(QStringLiteral(
"release-with-debug-info"),
386 QStringLiteral(
"Assume release binaries with debug information."));
387 releaseWithDebugInfoOption.setFlags(QCommandLineOption::HiddenFromHelp);
388 parser->addOption(releaseWithDebugInfoOption);
390 QCommandLineOption deployPdbOption(QStringLiteral(
"pdb"),
391 QStringLiteral(
"Deploy .pdb files (MSVC)."));
392 parser->addOption(deployPdbOption);
394 QCommandLineOption forceOption(QStringLiteral(
"force"),
395 QStringLiteral(
"Force updating files."));
396 parser->addOption(forceOption);
398 QCommandLineOption dryRunOption(QStringLiteral(
"dry-run"),
399 QStringLiteral(
"Simulation mode. Behave normally, but do not copy/update any files."));
400 parser->addOption(dryRunOption);
402 QCommandLineOption noPatchQtOption(QStringLiteral(
"no-patchqt"),
403 QStringLiteral(
"Do not patch the Qt6Core library."));
404 parser->addOption(noPatchQtOption);
406 QCommandLineOption ignoreErrorOption(QStringLiteral(
"ignore-library-errors"),
407 QStringLiteral(
"Ignore errors when libraries cannot be found."));
408 parser->addOption(ignoreErrorOption);
410 QCommandLineOption noPluginsOption(QStringLiteral(
"no-plugins"),
411 QStringLiteral(
"Skip plugin deployment."));
412 parser->addOption(noPluginsOption);
414 QCommandLineOption skipPluginTypesOption(QStringLiteral(
"skip-plugin-types"),
415 QStringLiteral(
"A comma-separated list of plugin types that are not deployed (qmltooling,generic)."),
416 QStringLiteral(
"plugin types"));
417 parser->addOption(skipPluginTypesOption);
419 QCommandLineOption addPluginTypesOption(QStringLiteral(
"add-plugin-types"),
420 QStringLiteral(
"A comma-separated list of plugin types that will be added to deployment (imageformats,iconengines)"),
421 QStringLiteral(
"plugin types"));
422 parser->addOption(addPluginTypesOption);
424 QCommandLineOption includePluginsOption(QStringLiteral(
"include-plugins"),
425 QStringLiteral(
"A comma-separated list of individual plugins that will be added to deployment (scene2d,qjpeg)"),
426 QStringLiteral(
"plugins"));
427 parser->addOption(includePluginsOption);
429 QCommandLineOption excludePluginsOption(QStringLiteral(
"exclude-plugins"),
430 QStringLiteral(
"A comma-separated list of individual plugins that will not be deployed (qsvg,qpdf)"),
431 QStringLiteral(
"plugins"));
432 parser->addOption(excludePluginsOption);
434 QCommandLineOption noLibraryOption(QStringLiteral(
"no-libraries"),
435 QStringLiteral(
"Skip library deployment."));
436 parser->addOption(noLibraryOption);
438 QCommandLineOption qmlDirOption(QStringLiteral(
"qmldir"),
439 QStringLiteral(
"Scan for QML-imports starting from directory."),
440 QStringLiteral(
"directory"));
441 parser->addOption(qmlDirOption);
443 QCommandLineOption qmlImportOption(QStringLiteral(
"qmlimport"),
444 QStringLiteral(
"Add the given path to the QML module search locations."),
445 QStringLiteral(
"directory"));
446 parser->addOption(qmlImportOption);
448 QCommandLineOption qmlImportTimeoutOption(QStringLiteral(
"qmlimporttimeout"),
449 QStringLiteral(
"Set timeout (in ms for) execution of "
450 "qmlimportscanner."),
451 QStringLiteral(
"timeout"));
452 parser->addOption(qmlImportTimeoutOption);
454 QCommandLineOption noQuickImportOption(QStringLiteral(
"no-quick-import"),
455 QStringLiteral(
"Skip deployment of Qt Quick imports."));
456 parser->addOption(noQuickImportOption);
459 QCommandLineOption translationOption(QStringLiteral(
"translations"),
460 QStringLiteral(
"A comma-separated list of languages to deploy (de,fi)."),
461 QStringLiteral(
"languages"));
462 parser->addOption(translationOption);
464 QCommandLineOption noTranslationOption(QStringLiteral(
"no-translations"),
465 QStringLiteral(
"Skip deployment of translations."));
466 parser->addOption(noTranslationOption);
468 QCommandLineOption noSystemD3DCompilerOption(QStringLiteral(
"no-system-d3d-compiler"),
469 QStringLiteral(
"Skip deployment of the system D3D compiler."));
470 parser->addOption(noSystemD3DCompilerOption);
472 QCommandLineOption noSystemDxcOption(QStringLiteral(
"no-system-dxc-compiler"),
473 QStringLiteral(
"Skip deployment of the system DXC (dxcompiler.dll, dxil.dll)."));
474 parser->addOption(noSystemDxcOption);
477 QCommandLineOption compilerRunTimeOption(QStringLiteral(
"compiler-runtime"),
478 QStringLiteral(
"Deploy compiler runtime (Desktop only)."));
479 parser->addOption(compilerRunTimeOption);
481 QCommandLineOption noCompilerRunTimeOption(QStringLiteral(
"no-compiler-runtime"),
482 QStringLiteral(
"Do not deploy compiler runtime (Desktop only)."));
483 parser->addOption(noCompilerRunTimeOption);
485 QCommandLineOption jsonOption(QStringLiteral(
"json"),
486 QStringLiteral(
"Print to stdout in JSON format."));
487 parser->addOption(jsonOption);
489 QCommandLineOption suppressSoftwareRasterizerOption(QStringLiteral(
"no-opengl-sw"),
490 QStringLiteral(
"Do not deploy the software rasterizer library."));
491 parser->addOption(suppressSoftwareRasterizerOption);
493 QCommandLineOption noFFmpegOption(QStringLiteral(
"no-ffmpeg"),
494 QStringLiteral(
"Do not deploy the FFmpeg libraries."));
495 parser->addOption(noFFmpegOption);
497 QCommandLineOption forceOpenSslOption(QStringLiteral(
"force-openssl"),
498 QStringLiteral(
"Deploy openssl plugin but ignore openssl library dependency"));
499 parser->addOption(forceOpenSslOption);
501 QCommandLineOption openSslRootOption(QStringLiteral(
"openssl-root"),
502 QStringLiteral(
"Directory containing openSSL libraries."),
503 QStringLiteral(
"directory"));
504 parser->addOption(openSslRootOption);
507 QCommandLineOption listOption(QStringLiteral(
"list"),
508 "Print only the names of the files copied.\n"
509 "Available options:\n"
510 " source: absolute path of the source files\n"
511 " target: absolute path of the target files\n"
512 " relative: paths of the target files, relative\n"
513 " to the target directory\n"
514 " mapping: outputs the source and the relative\n"
515 " target, suitable for use within an\n"
516 " Appx mapping file"_L1,
517 QStringLiteral(
"option"));
518 parser->addOption(listOption);
520 QCommandLineOption appxOption(QStringLiteral(
"appx"),
521 QStringLiteral(
"Create an appx package for the Windows Store"));
522 parser->addOption(appxOption);
524 QCommandLineOption appxCertificatePath(QStringLiteral(
"appx-certificate"),
525 QStringLiteral(
"Path to appx certificate to sign appx package"),
526 QStringLiteral(
".cer file"));
527 parser->addOption(appxCertificatePath);
530 parser->addOption(createVerboseOption());
532 parser->addPositionalArgument(QStringLiteral(
"[files]"),
533 QStringLiteral(
"Binaries or directory containing the binary."));
535 QCommandLineOption deployInsightTrackerOption(QStringLiteral(
"deploy-insighttracker"),
536 QStringLiteral(
"Deploy insight tracker plugin."));
538 bool insightTrackerModuleAvailable =
false;
540 OptionPtrVector enabledModuleOptions;
541 OptionPtrVector disabledModuleOptions;
543 enabledModuleOptions.reserve(qtModulesCount);
544 disabledModuleOptions.reserve(qtModulesCount);
545 for (
const QtModule &module : qtModuleEntries) {
546 const QString option = moduleNameToOptionName(module.name, module.internal);
547 const QString name = module.name;
548 if (name == u"InsightTracker") {
549 parser->addOption(deployInsightTrackerOption);
550 insightTrackerModuleAvailable =
true;
552 const QString enabledDescription = QStringLiteral(
"Add ") + name + QStringLiteral(
" module.");
553 CommandLineOptionPtr enabledOption(
new QCommandLineOption(option, enabledDescription));
554 parser->addOption(*enabledOption.data());
555 enabledModuleOptions.append(enabledOption);
556 const QString disabledDescription = QStringLiteral(
"Remove ") + name + QStringLiteral(
" module.");
557 CommandLineOptionPtr disabledOption(
new QCommandLineOption(QStringLiteral(
"no-") + option,
558 disabledDescription));
559 disabledModuleOptions.append(disabledOption);
560 parser->addOption(*disabledOption.data());
563 const bool success = parser->parse(arguments);
564 if (parser->isSet(helpOption))
566 if (parser->isSet(versionOption))
569 *errorMessage = parser->errorText();
573 options->libraryDirectory = parser->value(libDirOption);
574 options->pluginDirectory = parser->value(pluginDirOption);
575 options->translationsDirectory = parser->value(translationDirOption);
576 options->qmlDirectory = parser->value(qmlDeployDirOption);
577 options
->plugins = !parser->isSet(noPluginsOption);
578 options
->libraries = !parser->isSet(noLibraryOption);
580 if (parser->isSet(translationOption))
581 options->languages = parser->value(translationOption).split(u',');
583 options
->systemDxc = !parser->isSet(noSystemDxcOption);
587 if (!parser->isSet(appxCertificatePath)) {
588 *errorMessage = QStringLiteral(
"--appx requires --appx-certificate with a valid certificate");
591 options->appxCertificatePath = parser->value(appxCertificatePath);
596 || options->platform.testFlags(
WindowsDesktopMsvc) || parser->isSet(compilerRunTimeOption))
598 if (parser->isSet(noCompilerRunTimeOption))
604 *errorMessage = QStringLiteral(
"Deployment of the compiler runtime is implemented for "
605 "Desktop MSVC and MinGW (g++ and Clang) only.");
609 if (parser->isSet(skipPluginTypesOption))
610 options->pluginSelections.disabledPluginTypes = parser->value(skipPluginTypesOption).split(u',');
612 if (parser->isSet(addPluginTypesOption))
613 options->pluginSelections.enabledPluginTypes = parser->value(addPluginTypesOption).split(u',');
615 if (parser->isSet(includePluginsOption))
616 options->pluginSelections.includedPlugins = parser->value(includePluginsOption).split(u',');
618 if (parser->isSet(excludePluginsOption))
619 options->pluginSelections.excludedPlugins = parser->value(excludePluginsOption).split(u',');
621 if (parser->isSet(releaseWithDebugInfoOption))
622 std::wcerr <<
"Warning: " << releaseWithDebugInfoOption.names().first() <<
" is obsolete.";
624 switch (parseExclusiveOptions(parser, debugOption, releaseOption)) {
635 if (parser->isSet(deployPdbOption)) {
636 if (options->platform.testFlag(
WindowsBased) && !options->platform.testFlag(
MinGW))
639 std::wcerr <<
"Warning: --" << deployPdbOption.names().first() <<
" is not supported on this platform.\n";
642 if (parser->isSet(suppressSoftwareRasterizerOption))
645 if (parser->isSet(noFFmpegOption))
648 if (parser->isSet(forceOpenSslOption))
651 if (parser->isSet(openSslRootOption))
652 options->openSslRootDirectory = parser->value(openSslRootOption);
655 *errorMessage = QStringLiteral(
"force-openssl and openssl-root are mutually exclusive");
659 if (parser->isSet(forceOption))
661 if (parser->isSet(dryRunOption)) {
666 options
->patchQt = !parser->isSet(noPatchQtOption);
668 if (insightTrackerModuleAvailable)
671 for (
const QtModule &module : qtModuleEntries) {
672 if (parser->isSet(*enabledModuleOptions.at(module.id)))
673 options->additionalLibraries[module.id] = 1;
674 if (parser->isSet(*disabledModuleOptions.at(module.id)))
675 options->disabledLibraries[module.id] = 1;
679 if (options->additionalLibraries.test(QtQuickModuleId))
680 options->additionalLibraries[QtQmlModuleId] = 1;
681 if (options->additionalLibraries.test(QtDesignerComponentsModuleId))
682 options->additionalLibraries[QtDesignerModuleId] = 1;
684 if (parser->isSet(listOption)) {
685 const QString value = parser->value(listOption);
686 if (value == QStringLiteral(
"source")) {
687 options->list = ListSource;
688 }
else if (value == QStringLiteral(
"target")) {
689 options->list = ListTarget;
690 }
else if (value == QStringLiteral(
"relative")) {
691 options->list = ListRelative;
692 }
else if (value == QStringLiteral(
"mapping")) {
693 options->list = ListMapping;
695 *errorMessage = QStringLiteral(
"Please specify a valid option for -list (source, target, relative, mapping).");
700 if (parser->isSet(jsonOption) || options->list) {
705 const QStringList posArgs = parser->positionalArguments();
706 if (posArgs.isEmpty()) {
707 *errorMessage = QStringLiteral(
"Please specify the binary or folder.");
711 if (parser->isSet(dirOption))
712 options->directory = parser->value(dirOption);
714 if (parser->isSet(qmlDirOption))
715 options->qmlDirectories = parser->values(qmlDirOption);
717 if (parser->isSet(qmlImportOption))
718 options->qmlImportPaths = parser->values(qmlImportOption);
720 if (parser->isSet(qmlImportTimeoutOption)) {
721 const QString timeoutString = parser->value(qmlImportTimeoutOption);
723 int timeout = timeoutString.toInt(&ok);
725 *errorMessage = u'"' + timeoutString + QStringLiteral(
"\" is not an acceptable timeout "
732 const QString &file = posArgs.front();
733 const QFileInfo fi(QDir::cleanPath(file));
735 *errorMessage = msgFileDoesNotExist(file);
739 if (!options->directory.isEmpty() && !fi.isFile()) {
740 *errorMessage = u'"' + file + QStringLiteral(
"\" is not an executable file.");
745 options->binaries.append(fi.absoluteFilePath());
746 if (options->directory.isEmpty())
747 options->directory = fi.absolutePath();
749 const QString binary = findBinary(fi.absoluteFilePath(), options->platform);
750 if (binary.isEmpty()) {
751 *errorMessage = QStringLiteral(
"Unable to find binary in \"") + file + u'"';
754 options->directory = fi.absoluteFilePath();
755 options->binaries.append(binary);
759 bool multipleDirs =
false;
760 for (
int i = 1; i < posArgs.size(); ++i) {
761 const QFileInfo fi(QDir::cleanPath(posArgs.at(i)));
762 const QString path = fi.absoluteFilePath();
764 *errorMessage = msgFileDoesNotExist(path);
768 const QStringList libraries =
769 findSharedLibraries(QDir(path), options->platform, MatchDebugOrRelease, QString());
770 for (
const QString &library : libraries)
771 options->binaries.append(path + u'/' + library);
773 if (!parser->isSet(dirOption) && fi.absolutePath() != options->directory)
775 options->binaries.append(path);
779 std::wcerr <<
"Warning: using binaries from different directories, deploying to following path: "
780 << options->directory <<
'\n' <<
"To disable this warning, use the --dir option\n";
782 if (options->translationsDirectory.isEmpty())
783 options->translationsDirectory = options->directory +
"/translations"_L1;
1112 const QString &qtPluginsDirName,
const QString &libraryLocation,
1113 const QString &infix,
DebugMatchMode debugMatchModeIn, Platform platform,
1114 QString *platformPlugin,
bool deployInsightTrackerPlugin,
1115 bool deployOpenSslPlugin)
1117 if (qtPluginsDirName.isEmpty())
1118 return QStringList();
1119 QDir pluginsDir(qtPluginsDirName);
1121 bool missingQtModulesAdded =
false;
1122 const QFileInfoList &pluginDirs = pluginsDir.entryInfoList(QStringList(u"*"_s), QDir::Dirs | QDir::NoDotAndDotDot);
1123 for (
const QFileInfo &subDirFi : pluginDirs) {
1124 const QString subDirName = subDirFi.fileName();
1125 const size_t module = qtModuleEntries.moduleIdForPluginType(subDirName);
1126 if (module == QtModule::InvalidId) {
1127 if (optVerboseLevel > 1) {
1128 std::wcerr <<
"No Qt module found for plugin type \"" << subDirName <<
"\".\n";
1132 const bool dueToModule = usedQtModules->test(module);
1133 if (dueToModule || needsPluginType(subDirName, pluginInfo, pluginSelections)) {
1134 const DebugMatchMode debugMatchMode = (module == QtWebEngineCoreModuleId)
1135 ? MatchDebugOrRelease
1137 QDir subDir(subDirFi.absoluteFilePath());
1138 if (optVerboseLevel)
1139 std::wcout <<
"Adding in plugin type " << subDirFi.baseName() <<
" for module: " << qtModuleEntries.moduleById(module).name <<
'\n';
1141 const bool isPlatformPlugin = subDirName ==
"platforms"_L1;
1142 const QStringList plugins =
1143 findSharedLibraries(subDir, platform, debugMatchMode, QString());
1144 for (
const QString &plugin : plugins) {
1145 ModuleBitset pluginNeededQtModules;
1146 const QString pluginPath =
1147 deployPlugin(plugin, subDir, dueToModule, debugMatchMode, &pluginNeededQtModules,
1148 disabledQtModules, pluginSelections, libraryLocation, infix,
1149 platform, deployInsightTrackerPlugin, deployOpenSslPlugin);
1150 if (!pluginPath.isEmpty()) {
1151 if (isPlatformPlugin && plugin.startsWith(u"qwindows"))
1152 *platformPlugin = subDir.absoluteFilePath(plugin);
1153 result.append(pluginPath);
1155 const ModuleBitset missingModules = (pluginNeededQtModules & ~*usedQtModules);
1156 if (missingModules.any()) {
1157 *usedQtModules |= missingModules;
1158 missingQtModulesAdded =
true;
1159 if (optVerboseLevel) {
1160 std::wcout <<
"Adding " << formatQtModules(missingModules).constData()
1161 <<
" for " << plugin <<
" from plugin type: " << subDirName <<
'\n';
1171 if (missingQtModulesAdded) {
1173 std::wcout <<
"Performing additional pass of finding Qt plugins due to updated Qt module list: "
1174 << formatQtModules(*usedQtModules).constData() <<
"\n";
1176 return findQtPlugins(usedQtModules, disabledQtModules, pluginInfo, pluginSelections, qtPluginsDirName,
1177 libraryLocation, infix, debugMatchModeIn, platform, platformPlugin,
1178 deployInsightTrackerPlugin, deployOpenSslPlugin);
1527 const QChar slash = u'/';
1529 const QString qtBinDir = qtpathsVariables.value(QStringLiteral(
"QT_INSTALL_BINS"));
1530 const QString libraryLocation = qtBinDir;
1531 const QString infix = qtpathsVariables.value(QLatin1StringView(
qmakeInfixKey));
1532 const int version = qtVersion(qtpathsVariables);
1535 if (optVerboseLevel > 1)
1536 std::wcout <<
"Qt binaries in " << QDir::toNativeSeparators(qtBinDir) <<
'\n';
1538 QStringList dependentQtLibs;
1539 QStringList dependentNonQtLibs;
1540 PeHeaderInfoStruct peHeaderInfo;
1542 if (!readPeExecutableInfo(options.binaries.first(), errorMessage, &peHeaderInfo))
1544 if (!findDependentQtLibraries(libraryLocation, options.binaries.first(), options.platform,
1545 errorMessage, &dependentQtLibs, &dependentNonQtLibs)) {
1548 for (
int b = 1; b < options.binaries.size(); ++b) {
1549 if (!findDependentQtLibraries(libraryLocation, options.binaries.at(b), options.platform,
1550 errorMessage, &dependentQtLibs, &dependentNonQtLibs)) {
1555 const QFileInfo fi(options.binaries.first());
1556 const QString canonicalBinPath = fi.canonicalPath();
1559 for (qsizetype i = 0; i < dependentNonQtLibs.size(); ++i) {
1560 const QString nonQtLib = dependentNonQtLibs.at(i);
1561 const QString path = canonicalBinPath + u'/' + nonQtLib;
1562 if (!QFileInfo::exists(path))
1566 std::wcout <<
"Adding local dependency" << path <<
'\n';
1568 if (!findDependentQtLibraries(libraryLocation, path, options.platform,
1569 errorMessage, &dependentQtLibs, &dependentNonQtLibs)) {
1581 if (options.platform.testFlag(
Msvc) || options.platform.testFlag(
ClangMsvc)) {
1582 result
.isDebug = peHeaderInfo.isDebug;
1597 for (
int m = 0; m < dependentQtLibs.size(); ++m) {
1598 const qint64 module = qtModule(dependentQtLibs.at(m), infix);
1600 result.directlyUsedQtLibraries[module] = 1;
1603 const bool usesQml = result.directlyUsedQtLibraries.test(QtQmlModuleId);
1604 const bool usesQuick = result.directlyUsedQtLibraries.test(QtQuickModuleId);
1605 const bool uses3DQuick = result.directlyUsedQtLibraries.test(Qt3DQuickModuleId);
1606 const bool usesQml2 = !(options.disabledLibraries.test(QtQmlModuleId))
1607 && (usesQml || usesQuick || uses3DQuick || (options.additionalLibraries.test(QtQmlModuleId)));
1610 std::wcout << QDir::toNativeSeparators(options.binaries.first()) <<
' '
1611 << peHeaderInfo.wordSize <<
" bit, " << (result.isDebug ?
"debug" :
"release")
1614 std::wcout <<
" [QML]";
1618 if (dependentQtLibs.isEmpty()) {
1619 *errorMessage = QDir::toNativeSeparators(options.binaries.first()) + QStringLiteral(
" does not seem to be a Qt executable.");
1627 QStringList qmlImportPaths = options.qmlImportPaths;
1629 qmlImportPaths << qtpathsVariables.value(QStringLiteral(
"QT_INSTALL_QML"));
1630 QStringList qmlDirectories = options.qmlDirectories;
1631 if (qmlDirectories.isEmpty()) {
1632 const QString qmlDirectory = findQmlDirectory(options.platform, options.directory);
1633 if (!qmlDirectory.isEmpty())
1634 qmlDirectories.append(qmlDirectory);
1636 for (
const QString &qmlDirectory : std::as_const(qmlDirectories)) {
1637 if (optVerboseLevel >= 1)
1638 std::wcout <<
"Scanning " << QDir::toNativeSeparators(qmlDirectory) <<
":\n";
1639 const QmlImportScanResult scanResult =
1640 runQmlImportScanner(qmlDirectory, qmlImportPaths,
1641 result.directlyUsedQtLibraries.test(QtWidgetsModuleId),
1642 options.platform, debugMatchMode, errorMessage,
1643 options.qmlImportTimeout);
1646 qmlScanResult.append(scanResult);
1648 for (
const QString &plugin : std::as_const(qmlScanResult.plugins)) {
1649 if (!findDependentQtLibraries(libraryLocation, plugin, options.platform,
1650 errorMessage, &dependentQtLibs,
nullptr)) {
1654 if (optVerboseLevel >= 1) {
1655 std::wcout <<
"QML imports:\n";
1656 for (
const QmlImportScanResult::Module &mod : std::as_const(qmlScanResult.modules)) {
1657 std::wcout <<
" '" << mod.name <<
"' "
1658 << QDir::toNativeSeparators(mod.sourcePath) <<
'\n';
1660 if (optVerboseLevel >= 2) {
1661 std::wcout <<
"QML plugins:\n";
1662 for (
const QString &p : std::as_const(qmlScanResult.plugins))
1663 std::wcout <<
" " << QDir::toNativeSeparators(p) <<
'\n';
1669 QString platformPlugin;
1672 QStringList deployedQtLibraries;
1673 for (
int i = 0 ; i < dependentQtLibs.size(); ++i) {
1674 const qint64 module = qtModule(dependentQtLibs.at(i), infix);
1676 result.usedQtLibraries[module] = 1;
1678 deployedQtLibraries.push_back(dependentQtLibs.at(i));
1680 result.deployedQtLibraries = (result.usedQtLibraries | options.additionalLibraries) & ~options.disabledLibraries;
1682 ModuleBitset disabled = options.disabledLibraries;
1684 disabled[QtQmlModuleId] = 1;
1685 disabled[QtQuickModuleId] = 1;
1691 const QStringList qtLibs = dependentQtLibs.filter(QStringLiteral(
"Qt6Core"), Qt::CaseInsensitive)
1692 + dependentQtLibs.filter(QStringLiteral(
"Qt5WebKit"), Qt::CaseInsensitive);
1693 for (
const QString &qtLib : qtLibs) {
1694 QStringList icuLibs = findDependentLibraries(qtLib, errorMessage).filter(QStringLiteral(
"ICU"), Qt::CaseInsensitive);
1695 if (!icuLibs.isEmpty()) {
1698 const QString icuVersion = getIcuVersion(icuLibs.constFirst());
1699 if (!icuVersion.isEmpty()) {
1700 if (optVerboseLevel > 1)
1701 std::wcout <<
"Adding ICU version " << icuVersion <<
'\n';
1702 QString icuLib = QStringLiteral(
"icudt") + icuVersion
1703 + QLatin1StringView(windowsSharedLibrarySuffix);
1706 if (result.isDebug) {
1707 const QString icuLibCandidate = QStringLiteral(
"icudtd") + icuVersion
1708 + QLatin1StringView(windowsSharedLibrarySuffix);
1709 if (!findInPath(icuLibCandidate).isEmpty()) {
1710 icuLib = icuLibCandidate;
1713 icuLibs.push_back(icuLib);
1715 for (
const QString &icuLib : std::as_const(icuLibs)) {
1716 const QString icuPath = findInPath(icuLib);
1717 if (icuPath.isEmpty()) {
1718 *errorMessage = QStringLiteral(
"Unable to locate ICU library ") + icuLib;
1721 deployedQtLibraries.push_back(icuPath);
1728 QStringList openSslLibs;
1729 if (!options.openSslRootDirectory.isEmpty()) {
1730 openSslLibs = findOpenSslLibraries(options.openSslRootDirectory, options.platform);
1731 if (openSslLibs.isEmpty()) {
1732 *errorMessage = QStringLiteral(
"Unable to find openSSL libraries in ")
1733 + options.openSslRootDirectory;
1737 deployedQtLibraries.append(openSslLibs);
1741 const QStringList plugins = findQtPlugins(
1742 &result.deployedQtLibraries,
1745 disabled, pluginInfo,
1746 options.pluginSelections, qtpathsVariables.value(QStringLiteral(
"QT_INSTALL_PLUGINS")),
1747 libraryLocation, infix, debugMatchMode, options.platform, &platformPlugin,
1748 options.deployInsightTrackerPlugin, deployOpenSslPlugin);
1751 QString qtGuiLibrary;
1752 for (
const auto &qtModule : qtModuleEntries) {
1753 if (result.deployedQtLibraries.test(qtModule.id)) {
1754 const QString library = libraryPath(libraryLocation, qtModule.name.toUtf8(), infix,
1755 options.platform, result.isDebug);
1756 deployedQtLibraries.append(library);
1757 if (qtModule.id == QtGuiModuleId)
1758 qtGuiLibrary = library;
1763 std::wcout <<
"Direct dependencies: " << formatQtModules(result.directlyUsedQtLibraries).constData()
1764 <<
"\nAll dependencies : " << formatQtModules(result.usedQtLibraries).constData()
1765 <<
"\nTo be deployed : " << formatQtModules(result.deployedQtLibraries).constData() <<
'\n';
1769 std::wcout <<
"Plugins: " << plugins.join(u',') <<
'\n';
1771 if (result.deployedQtLibraries.test(QtGuiModuleId) && platformPlugin.isEmpty()) {
1772 *errorMessage =QStringLiteral(
"Unable to find the platform plugin.");
1776 if (options.platform.testFlag(
WindowsBased) && !qtGuiLibrary.isEmpty()) {
1777 const QStringList guiLibraries = findDependentLibraries(qtGuiLibrary, errorMessage);
1778 const bool dependsOnOpenGl = !guiLibraries.filter(QStringLiteral(
"opengl32"), Qt::CaseInsensitive).isEmpty();
1780 const QFileInfo softwareRasterizer(qtBinDir + slash + QStringLiteral(
"opengl32sw") + QLatin1StringView(
windowsSharedLibrarySuffix));
1781 if (softwareRasterizer.isFile())
1782 deployedQtLibraries.append(softwareRasterizer.absoluteFilePath());
1785 const QString d3dCompiler = findD3dCompiler(options.platform, qtBinDir,
1786 peHeaderInfo.wordSize);
1787 if (d3dCompiler.isEmpty()) {
1788 std::wcerr <<
"Warning: Cannot find any version of the d3dcompiler DLL.\n";
1790 deployedQtLibraries.push_back(d3dCompiler);
1794 const QStringList dxcLibs = findDxc(options.platform, qtBinDir,
1795 peHeaderInfo.wordSize);
1796 if (!dxcLibs.isEmpty()) {
1797 deployedQtLibraries.append(dxcLibs);
1799 std::wcerr <<
"Warning: Cannot find any version of the dxcompiler.dll and dxil.dll."
1800 <<
" This is not a problem unless Direct3D 12 and certain features are used by the application.\n";
1807 && !plugins.filter(QStringLiteral(
"ffmpegmediaplugin"), Qt::CaseInsensitive).empty()) {
1808 deployedQtLibraries.append(findFFmpegLibs(qtBinDir, options.platform));
1813 const QString targetPath = options.libraryDirectory.isEmpty() ?
1814 options.directory : options.libraryDirectory;
1815 QStringList libraries = deployedQtLibraries;
1817 libraries.append(compilerRunTimeLibs(qtBinDir, options.platform, result.isDebug,
1818 peHeaderInfo.machineArch));
1820 for (
const QString &qtLib : std::as_const(libraries)) {
1821 if (isSystemLibrary(qtLib)) {
1822 std::wcout <<
"Skipping system library " << qtLib <<
"\n";
1825 if (!updateLibrary(qtLib, targetPath, options, errorMessage))
1829#if !QT_CONFIG(relocatable)
1830 if (options.patchQt && !options.dryRun) {
1831 const QString qt6CoreName = QFileInfo(libraryPath(libraryLocation,
"Qt6Core", infix,
1832 options.platform, result.isDebug)).fileName();
1833 if (!patchQtCore(targetPath + u'/' + qt6CoreName, errorMessage)) {
1834 std::wcerr <<
"Warning: " << *errorMessage <<
'\n';
1835 errorMessage->clear();
1843 const QString targetPath = options.pluginDirectory.isEmpty() ?
1844 options.directory : options.pluginDirectory;
1845 QDir dir(targetPath);
1846 if (!dir.exists() && !dir.mkpath(QStringLiteral(
"."))) {
1847 *errorMessage =
"Cannot create "_L1 +
1848 QDir::toNativeSeparators(dir.absolutePath()) + u'.';
1851 for (
const QString &plugin : plugins) {
1852 const QString targetDirName = plugin.section(slash, -2, -2);
1853 const QString targetPath = dir.absoluteFilePath(targetDirName);
1854 if (!dir.exists(targetDirName)) {
1855 if (optVerboseLevel)
1856 std::wcout <<
"Creating directory " << targetPath <<
".\n";
1857 if (!(options.updateFileFlags & SkipUpdateFile) && !dir.mkdir(targetDirName)) {
1858 *errorMessage = QStringLiteral(
"Cannot create ") + targetDirName + u'.';
1862 if (!updateLibrary(plugin, targetPath, options, errorMessage))
1871 const QString targetPath = options.qmlDirectory.isEmpty()
1872 ? options.directory + QStringLiteral(
"/qml")
1873 : options.qmlDirectory;
1874 if (!createDirectory(targetPath, errorMessage, options.dryRun))
1876 for (
const QmlImportScanResult::Module &module : std::as_const(qmlScanResult.modules)) {
1877 const QString installPath = module.installPath(targetPath);
1878 if (optVerboseLevel > 1)
1879 std::wcout <<
"Installing: '" << module.name
1880 <<
"' from " << module.sourcePath <<
" to "
1881 << QDir::toNativeSeparators(installPath) <<
'\n';
1882 if (installPath != targetPath && !createDirectory(installPath, errorMessage, options.dryRun))
1884 unsigned updateFileFlags = options.updateFileFlags
1885 | SkipQmlDesignerSpecificsDirectories;
1886 unsigned qmlDirectoryFileFlags = 0;
1887 if (options.deployPdb)
1888 qmlDirectoryFileFlags |= QmlDirectoryFileEntryFunction::DeployPdb;
1889 if (!updateFile(module.sourcePath, QmlDirectoryFileEntryFunction(module.sourcePath,
1892 qmlDirectoryFileFlags),
1893 installPath, updateFileFlags, options.json, errorMessage)) {
1900 if (!createDirectory(options.translationsDirectory, errorMessage, options.dryRun))
1902 if (!deployTranslations(qtpathsVariables.value(QStringLiteral(
"QT_INSTALL_TRANSLATIONS")),
1903 result.deployedQtLibraries, options.translationsDirectory, options,
1936 bool isDebug, QString *errorMessage)
1938 static const char *installDataFilesRelease[] = {
1939 "icudtl.dat",
"qtwebengine_devtools_resources.pak",
"qtwebengine_resources.pak",
1940 "qtwebengine_resources_100p.pak",
"qtwebengine_resources_200p.pak"
1942 static const char *installDataFilesDebug[] = {
1943 "icudtl.dat",
"qtwebengine_devtools_resources.debug.pak",
"qtwebengine_resources.debug.pak",
1944 "qtwebengine_resources_100p.debug.pak",
"qtwebengine_resources_200p.debug.pak"
1946 static const auto &installDataFiles = isDebug ? installDataFilesDebug : installDataFilesRelease;
1947 static const auto installV8SnapshotFile =
1948 isDebug ?
"v8_context_snapshot.debug.bin" :
"v8_context_snapshot.bin";
1951 if (isDebug && platformHasDebugSuffix(options.platform))
1952 webEngineProcessName.append(
'd');
1954 std::wcout <<
"Deploying: " << webEngineProcessName.constData() <<
"...\n";
1955 if (!deployWebProcess(qtpathsVariables, webEngineProcessName, pluginInfo, options, errorMessage))
1957 const QString resourcesSubDir = QStringLiteral(
"/resources");
1958 const QString resourcesSourceDir = qtpathsVariables.value(QStringLiteral(
"QT_INSTALL_DATA"))
1959 + resourcesSubDir + u'/';
1960 const QString resourcesTargetDir(options.directory + resourcesSubDir);
1961 if (!createDirectory(resourcesTargetDir, errorMessage, options.dryRun))
1963 for (
auto installDataFile : installDataFiles) {
1964 if (!updateFile(resourcesSourceDir + QLatin1StringView(installDataFile),
1965 resourcesTargetDir, options.updateFileFlags, options.json, errorMessage)) {
1970 updateFile(resourcesSourceDir + QLatin1StringView(installV8SnapshotFile), resourcesTargetDir,
1972 errorMessage->clear();
1973 const QFileInfo translations(qtpathsVariables.value(QStringLiteral(
"QT_INSTALL_TRANSLATIONS"))
1974 + QStringLiteral(
"/qtwebengine_locales"));
1975 if (!translations.isDir()) {
1976 std::wcerr <<
"Warning: Cannot find the translation files of the QtWebEngine module at "
1977 << QDir::toNativeSeparators(translations.absoluteFilePath()) <<
".\n";
1982 return createDirectory(options.translationsDirectory, errorMessage, options.dryRun)
1983 && updateFile(translations.absoluteFilePath(), options.translationsDirectory,
1984 options.updateFileFlags, options.json, errorMessage);
1987 const QFileInfo enUSpak(translations.filePath() + QStringLiteral(
"/en-US.pak"));
1988 if (!enUSpak.exists()) {
1989 std::wcerr <<
"Warning: Cannot find "
1990 << QDir::toNativeSeparators(enUSpak.absoluteFilePath()) <<
".\n";
1993 const QString webEngineTranslationsDir = options.translationsDirectory + u'/'
1994 + translations.fileName();
1995 if (!createDirectory(webEngineTranslationsDir, errorMessage, options.dryRun))
1997 return updateFile(enUSpak.absoluteFilePath(), webEngineTranslationsDir,
1998 options.updateFileFlags, options.json, errorMessage);
2007 QCoreApplication a(argc, argv);
2008 QCoreApplication::setApplicationVersion(QT_VERSION_STR
""_L1);
2010 const QByteArray qtBinPath = QFile::encodeName(QDir::toNativeSeparators(QCoreApplication::applicationDirPath()));
2011 QByteArray path = qgetenv(
"PATH");
2012 if (!path.contains(qtBinPath)) {
2013 path.prepend(QDir::listSeparator().toLatin1());
2014 path.prepend(qtBinPath);
2015 qputenv(
"PATH", path);
2019 QString errorMessage;
2024 int result = parseEarlyArguments(QCoreApplication::arguments(), &options, &errorMessage);
2026 std::wcerr <<
"Error: " << errorMessage <<
"\n";
2031 const QMap<QString, QString> qtpathsVariables =
2032 queryQtPaths(options.qtpathsBinary, &errorMessage);
2033 const QString xSpec = qtpathsVariables.value(QStringLiteral(
"QMAKE_XSPEC"));
2034 if (qtpathsVariables.isEmpty() || xSpec.isEmpty()
2035 || !qtpathsVariables.contains(QStringLiteral(
"QT_INSTALL_BINS"))) {
2036 std::wcerr <<
"Unable to query qtpaths: " << errorMessage <<
'\n';
2040 options.platform = platformFromMkSpec(xSpec);
2045 switch (si.wProcessorArchitecture) {
2046 case PROCESSOR_ARCHITECTURE_INTEL:
2047 case PROCESSOR_ARCHITECTURE_IA64:
2048 case PROCESSOR_ARCHITECTURE_AMD64:
2051 case PROCESSOR_ARCHITECTURE_ARM:
2052 case PROCESSOR_ARCHITECTURE_ARM64:
2060 std::wcerr <<
"Unsupported platform " << xSpec <<
'\n';
2065 const QString modulesDir
2066 = qtpathsVariables.value(QLatin1String(
"QT_INSTALL_ARCHDATA"))
2067 + QLatin1String(
"/modules");
2068 const QString translationsDir
2069 = qtpathsVariables.value(QLatin1String(
"QT_INSTALL_TRANSLATIONS"));
2072 std::wcerr <<
"Error: " << errorMessage <<
"\n";
2079 pluginInfo.generateAvailablePlugins(qtpathsVariables, options.platform);
2083 QCommandLineParser parser;
2084 QString errorMessage;
2085 const int result = parseArguments(QCoreApplication::arguments(), &parser, &options, &errorMessage);
2087 std::wcerr << errorMessage <<
"\n\n";
2089 std::fputs(QT_VERSION_STR
"\n", stdout);
2093 std::fputs(qPrintable(helpText(parser, pluginInfo)), stdout);
2101 if (!createDirectory(options.directory, &errorMessage, options.dryRun)) {
2102 std::wcerr << errorMessage <<
'\n';
2105 if (!options.libraryDirectory.isEmpty() && options.libraryDirectory != options.directory
2106 && !createDirectory(options.libraryDirectory, &errorMessage, options.dryRun)) {
2107 std::wcerr << errorMessage <<
'\n';
2111 const DeployResult result = deploy(options, qtpathsVariables, pluginInfo, &errorMessage);
2113 std::wcerr << errorMessage <<
'\n';
2117 if (result.deployedQtLibraries.test(QtWebEngineCoreModuleId)) {
2118 if (!deployWebEngineCore(qtpathsVariables, pluginInfo, options, result.isDebug,
2120 std::wcerr << errorMessage <<
'\n';
2125 if (options
.createAppx && !options.appxCertificatePath.isEmpty()) {
2126 const QFileInfo storeLogo(options.directory + QStringLiteral(
"/Assets/StoreLogo.png"));
2127 if (!storeLogo.exists()) {
2128 std::wcerr <<
"Error: Could not open application logo file " << storeLogo.absoluteFilePath() <<
'\n';
2132 QFile certFile(options.appxCertificatePath);
2133 if (!certFile.open(QIODevice::ReadOnly)) {
2134 std::wcerr <<
"Could not open certificate file" <<
'\n';
2138 QSslCertificate cert(&certFile, QSsl::Der);
2139 QString publisher = cert.subjectDisplayName();
2141 const QString applicationName = QFileInfo(options.binaries.first()).baseName();
2142 const QString platform = options.platform.testFlag(
PlatformFlag::IntelBased) ? QStringLiteral(
"x64") : QStringLiteral(
"arm64");
2143 const QString appxFilePath(options.directory + QStringLiteral(
"/") + QStringLiteral(
"AppxManifest.xml"));
2144 QFile f(appxFilePath);
2145 if (!f.open(QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text)) {
2146 std::wcerr <<
"Could not create AppxManifest.xml" <<
'\n';
2150 QXmlStreamWriter manifestWriter(&f);
2151 manifestWriter.setAutoFormatting(
true);
2152 manifestWriter.writeStartDocument();
2153 manifestWriter.writeStartElement(QStringLiteral(
"Package"));
2154 manifestWriter.writeAttribute(QStringLiteral(
"xmlns"), QStringLiteral(
"http://schemas.microsoft.com/appx/manifest/foundation/windows10"));
2155 manifestWriter.writeAttribute(QStringLiteral(
"xmlns:uap"), QStringLiteral(
"http://schemas.microsoft.com/appx/manifest/uap/windows10"));
2156 manifestWriter.writeAttribute(QStringLiteral(
"xmlns:rescap"), QStringLiteral(
"http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"));
2158 manifestWriter.writeStartElement(QStringLiteral(
"Identity"));
2159 manifestWriter.writeAttribute(
"Name", QUuid::createUuid().toString(QUuid::WithoutBraces));
2160 manifestWriter.writeAttribute(
"Publisher", QStringLiteral(
"CN=") + publisher);
2161 manifestWriter.writeAttribute(
"Version",
"1.0.0.0");
2162 manifestWriter.writeAttribute(
"ProcessorArchitecture", platform);
2163 manifestWriter.writeEndElement();
2165 manifestWriter.writeStartElement(
"Properties");
2166 manifestWriter.writeStartElement(
"DisplayName");
2167 manifestWriter.writeCharacters(applicationName);
2168 manifestWriter.writeEndElement();
2169 manifestWriter.writeStartElement(
"PublisherDisplayName");
2170 manifestWriter.writeCharacters(publisher);
2171 manifestWriter.writeEndElement();
2172 manifestWriter.writeStartElement(
"Logo");
2173 manifestWriter.writeCharacters(
"Assets/StoreLogo.png");
2174 manifestWriter.writeEndElement();
2175 manifestWriter.writeEndElement();
2177 manifestWriter.writeStartElement(
"Dependencies");
2178 manifestWriter.writeStartElement(
"TargetDeviceFamily");
2179 manifestWriter.writeAttribute(
"Name",
"Windows.Desktop");
2180 manifestWriter.writeAttribute(
"MinVersion",
"10.0.14316.0");
2181 manifestWriter.writeAttribute(
"MaxVersionTested",
"10.0.14316.0");
2182 manifestWriter.writeEndElement();
2183 manifestWriter.writeEndElement();
2185 manifestWriter.writeStartElement(
"Capabilities");
2186 manifestWriter.writeStartElement(
"rescap:Capability");
2187 manifestWriter.writeAttribute(
"Name",
"runFullTrust");
2188 manifestWriter.writeEndElement();
2189 manifestWriter.writeEndElement();
2191 manifestWriter.writeStartElement(
"Resources");
2192 manifestWriter.writeStartElement(
"Resource");
2193 if (options.languages.isEmpty()) {
2194 QLocale locale = QLocale::system();
2195 manifestWriter.writeAttribute(
"Language", locale.bcp47Name());
2197 for (
const auto& language : options.languages) {
2198 manifestWriter.writeAttribute(
"Language", language);
2201 manifestWriter.writeEndElement();
2202 manifestWriter.writeEndElement();
2204 manifestWriter.writeStartElement(
"Applications");
2205 for (
const auto& binary : options.binaries) {
2206 const QString binaryRelative = binary.split(QStringLiteral(
"/")).last();
2207 const QString displayName = binaryRelative.split(QStringLiteral(
".")).first();
2208 QFile descriptionFile(options.directory + QStringLiteral(
"/") + QStringLiteral(
"Assets/Description_") + displayName + QStringLiteral(
".txt"));
2209 QString description;
2210 if (!descriptionFile.exists())
2211 std::wcerr <<
"Warning: No package description was provided " << descriptionFile.fileName() <<
'\n';
2212 if (descriptionFile.open(QIODevice::ReadOnly | QIODevice::Text))
2213 description = QString::fromUtf8(descriptionFile.readAll());
2215 manifestWriter.writeStartElement(
"Application");
2216 manifestWriter.writeAttribute(
"Id", displayName);
2217 manifestWriter.writeAttribute(
"Executable", binaryRelative);
2218 manifestWriter.writeAttribute(
"EntryPoint",
"Windows.FullTrustApplication");
2219 manifestWriter.writeStartElement(
"uap:VisualElements");
2220 manifestWriter.writeAttribute(
"DisplayName", displayName);
2221 manifestWriter.writeAttribute(
"Description", description);
2222 manifestWriter.writeAttribute(
"BackgroundColor",
"transparent");
2223 manifestWriter.writeAttribute(
"Square150x150Logo",
"Assets/Logo.png");
2224 manifestWriter.writeAttribute(
"Square44x44Logo",
"Assets/SmallLogo.png");
2225 manifestWriter.writeStartElement(
"uap:DefaultTile");
2226 manifestWriter.writeEndElement();
2227 manifestWriter.writeEndElement();
2228 manifestWriter.writeEndElement();
2230 manifestWriter.writeEndElement();
2231 manifestWriter.writeEndElement();
2232 manifestWriter.writeEndDocument();
2237 std::fputs(options
.json->toList(options.list, options.directory).constData(), stdout);
2239 std::fputs(options
.json->toJson().constData(), stdout);
2240 delete options
.json;
2241 options
.json =
nullptr;