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;
1104 const QString &qtPluginsDirName,
const QString &libraryLocation,
1105 const QString &infix,
DebugMatchMode debugMatchModeIn, Platform platform,
1106 QString *platformPlugin,
bool deployInsightTrackerPlugin,
1107 bool deployOpenSslPlugin)
1109 if (qtPluginsDirName.isEmpty())
1110 return QStringList();
1111 QDir pluginsDir(qtPluginsDirName);
1113 bool missingQtModulesAdded =
false;
1114 const QFileInfoList &pluginDirs = pluginsDir.entryInfoList(QStringList(u"*"_s), QDir::Dirs | QDir::NoDotAndDotDot);
1115 for (
const QFileInfo &subDirFi : pluginDirs) {
1116 const QString subDirName = subDirFi.fileName();
1117 const size_t module = qtModuleEntries.moduleIdForPluginType(subDirName);
1118 if (module == QtModule::InvalidId) {
1119 if (optVerboseLevel > 1) {
1120 std::wcerr <<
"No Qt module found for plugin type \"" << subDirName <<
"\".\n";
1124 const bool dueToModule = usedQtModules->test(module);
1125 if (dueToModule || needsPluginType(subDirName, pluginInfo, pluginSelections)) {
1126 const DebugMatchMode debugMatchMode = (module == QtWebEngineCoreModuleId)
1127 ? MatchDebugOrRelease
1129 QDir subDir(subDirFi.absoluteFilePath());
1130 if (optVerboseLevel)
1131 std::wcout <<
"Adding in plugin type " << subDirFi.baseName() <<
" for module: " << qtModuleEntries.moduleById(module).name <<
'\n';
1133 const bool isPlatformPlugin = subDirName ==
"platforms"_L1;
1134 const QStringList plugins =
1135 findSharedLibraries(subDir, platform, debugMatchMode, QString());
1136 for (
const QString &plugin : plugins) {
1137 ModuleBitset pluginNeededQtModules;
1138 const QString pluginPath =
1139 deployPlugin(plugin, subDir, dueToModule, debugMatchMode, &pluginNeededQtModules,
1140 disabledQtModules, pluginSelections, libraryLocation, infix,
1141 platform, deployInsightTrackerPlugin, deployOpenSslPlugin);
1142 if (!pluginPath.isEmpty()) {
1143 if (isPlatformPlugin && plugin.startsWith(u"qwindows"))
1144 *platformPlugin = subDir.absoluteFilePath(plugin);
1145 result.append(pluginPath);
1147 const ModuleBitset missingModules = (pluginNeededQtModules & ~*usedQtModules);
1148 if (missingModules.any()) {
1149 *usedQtModules |= missingModules;
1150 missingQtModulesAdded =
true;
1151 if (optVerboseLevel) {
1152 std::wcout <<
"Adding " << formatQtModules(missingModules).constData()
1153 <<
" for " << plugin <<
" from plugin type: " << subDirName <<
'\n';
1163 if (missingQtModulesAdded) {
1165 std::wcout <<
"Performing additional pass of finding Qt plugins due to updated Qt module list: "
1166 << formatQtModules(*usedQtModules).constData() <<
"\n";
1168 return findQtPlugins(usedQtModules, disabledQtModules, pluginInfo, pluginSelections, qtPluginsDirName,
1169 libraryLocation, infix, debugMatchModeIn, platform, platformPlugin,
1170 deployInsightTrackerPlugin, deployOpenSslPlugin);
1519 const QChar slash = u'/';
1521 const QString qtBinDir = qtpathsVariables.value(QStringLiteral(
"QT_INSTALL_BINS"));
1522 const QString libraryLocation = qtBinDir;
1523 const QString infix = qtpathsVariables.value(QLatin1StringView(
qmakeInfixKey));
1524 const int version = qtVersion(qtpathsVariables);
1527 if (optVerboseLevel > 1)
1528 std::wcout <<
"Qt binaries in " << QDir::toNativeSeparators(qtBinDir) <<
'\n';
1530 QStringList dependentQtLibs;
1531 QStringList dependentNonQtLibs;
1532 PeHeaderInfoStruct peHeaderInfo;
1534 if (!readPeExecutableInfo(options.binaries.first(), errorMessage, &peHeaderInfo))
1536 if (!findDependentQtLibraries(libraryLocation, options.binaries.first(), options.platform,
1537 errorMessage, &dependentQtLibs, &dependentNonQtLibs)) {
1540 for (
int b = 1; b < options.binaries.size(); ++b) {
1541 if (!findDependentQtLibraries(libraryLocation, options.binaries.at(b), options.platform,
1542 errorMessage, &dependentQtLibs, &dependentNonQtLibs)) {
1547 const QFileInfo fi(options.binaries.first());
1548 const QString canonicalBinPath = fi.canonicalPath();
1551 for (qsizetype i = 0; i < dependentNonQtLibs.size(); ++i) {
1552 const QString nonQtLib = dependentNonQtLibs.at(i);
1553 const QString path = canonicalBinPath + u'/' + nonQtLib;
1554 if (!QFileInfo::exists(path))
1558 std::wcout <<
"Adding local dependency" << path <<
'\n';
1560 if (!findDependentQtLibraries(libraryLocation, path, options.platform,
1561 errorMessage, &dependentQtLibs, &dependentNonQtLibs)) {
1573 if (options.platform.testFlag(
Msvc) || options.platform.testFlag(
ClangMsvc)) {
1574 result
.isDebug = peHeaderInfo.isDebug;
1589 for (
int m = 0; m < dependentQtLibs.size(); ++m) {
1590 const qint64 module = qtModule(dependentQtLibs.at(m), infix);
1592 result.directlyUsedQtLibraries[module] = 1;
1595 const bool usesQml = result.directlyUsedQtLibraries.test(QtQmlModuleId);
1596 const bool usesQuick = result.directlyUsedQtLibraries.test(QtQuickModuleId);
1597 const bool uses3DQuick = result.directlyUsedQtLibraries.test(Qt3DQuickModuleId);
1598 const bool usesQml2 = !(options.disabledLibraries.test(QtQmlModuleId))
1599 && (usesQml || usesQuick || uses3DQuick || (options.additionalLibraries.test(QtQmlModuleId)));
1602 std::wcout << QDir::toNativeSeparators(options.binaries.first()) <<
' '
1603 << peHeaderInfo.wordSize <<
" bit, " << (result.isDebug ?
"debug" :
"release")
1606 std::wcout <<
" [QML]";
1610 if (dependentQtLibs.isEmpty()) {
1611 *errorMessage = QDir::toNativeSeparators(options.binaries.first()) + QStringLiteral(
" does not seem to be a Qt executable.");
1619 QStringList qmlImportPaths = options.qmlImportPaths;
1621 qmlImportPaths << qtpathsVariables.value(QStringLiteral(
"QT_INSTALL_QML"));
1622 QStringList qmlDirectories = options.qmlDirectories;
1623 if (qmlDirectories.isEmpty()) {
1624 const QString qmlDirectory = findQmlDirectory(options.platform, options.directory);
1625 if (!qmlDirectory.isEmpty())
1626 qmlDirectories.append(qmlDirectory);
1628 for (
const QString &qmlDirectory : std::as_const(qmlDirectories)) {
1629 if (optVerboseLevel >= 1)
1630 std::wcout <<
"Scanning " << QDir::toNativeSeparators(qmlDirectory) <<
":\n";
1631 const QmlImportScanResult scanResult =
1632 runQmlImportScanner(qmlDirectory, qmlImportPaths,
1633 result.directlyUsedQtLibraries.test(QtWidgetsModuleId),
1634 options.platform, debugMatchMode, errorMessage,
1635 options.qmlImportTimeout);
1638 qmlScanResult.append(scanResult);
1640 for (
const QString &plugin : std::as_const(qmlScanResult.plugins)) {
1641 if (!findDependentQtLibraries(libraryLocation, plugin, options.platform,
1642 errorMessage, &dependentQtLibs,
nullptr)) {
1646 if (optVerboseLevel >= 1) {
1647 std::wcout <<
"QML imports:\n";
1648 for (
const QmlImportScanResult::Module &mod : std::as_const(qmlScanResult.modules)) {
1649 std::wcout <<
" '" << mod.name <<
"' "
1650 << QDir::toNativeSeparators(mod.sourcePath) <<
'\n';
1652 if (optVerboseLevel >= 2) {
1653 std::wcout <<
"QML plugins:\n";
1654 for (
const QString &p : std::as_const(qmlScanResult.plugins))
1655 std::wcout <<
" " << QDir::toNativeSeparators(p) <<
'\n';
1661 QString platformPlugin;
1664 QStringList deployedQtLibraries;
1665 for (
int i = 0 ; i < dependentQtLibs.size(); ++i) {
1666 const qint64 module = qtModule(dependentQtLibs.at(i), infix);
1668 result.usedQtLibraries[module] = 1;
1670 deployedQtLibraries.push_back(dependentQtLibs.at(i));
1672 result.deployedQtLibraries = (result.usedQtLibraries | options.additionalLibraries) & ~options.disabledLibraries;
1674 ModuleBitset disabled = options.disabledLibraries;
1676 disabled[QtQmlModuleId] = 1;
1677 disabled[QtQuickModuleId] = 1;
1683 const QStringList qtLibs = dependentQtLibs.filter(QStringLiteral(
"Qt6Core"), Qt::CaseInsensitive)
1684 + dependentQtLibs.filter(QStringLiteral(
"Qt5WebKit"), Qt::CaseInsensitive);
1685 for (
const QString &qtLib : qtLibs) {
1686 QStringList icuLibs = findDependentLibraries(qtLib, errorMessage).filter(QStringLiteral(
"ICU"), Qt::CaseInsensitive);
1687 if (!icuLibs.isEmpty()) {
1690 const QString icuVersion = getIcuVersion(icuLibs.constFirst());
1691 if (!icuVersion.isEmpty()) {
1692 if (optVerboseLevel > 1)
1693 std::wcout <<
"Adding ICU version " << icuVersion <<
'\n';
1694 QString icuLib = QStringLiteral(
"icudt") + icuVersion
1695 + QLatin1StringView(windowsSharedLibrarySuffix);
1698 if (result.isDebug) {
1699 const QString icuLibCandidate = QStringLiteral(
"icudtd") + icuVersion
1700 + QLatin1StringView(windowsSharedLibrarySuffix);
1701 if (!findInPath(icuLibCandidate).isEmpty()) {
1702 icuLib = icuLibCandidate;
1705 icuLibs.push_back(icuLib);
1707 for (
const QString &icuLib : std::as_const(icuLibs)) {
1708 const QString icuPath = findInPath(icuLib);
1709 if (icuPath.isEmpty()) {
1710 *errorMessage = QStringLiteral(
"Unable to locate ICU library ") + icuLib;
1713 deployedQtLibraries.push_back(icuPath);
1720 QStringList openSslLibs;
1721 if (!options.openSslRootDirectory.isEmpty()) {
1722 openSslLibs = findOpenSslLibraries(options.openSslRootDirectory, options.platform);
1723 if (openSslLibs.isEmpty()) {
1724 *errorMessage = QStringLiteral(
"Unable to find openSSL libraries in ")
1725 + options.openSslRootDirectory;
1729 deployedQtLibraries.append(openSslLibs);
1733 const QStringList plugins = findQtPlugins(
1734 &result.deployedQtLibraries,
1737 disabled, pluginInfo,
1738 options.pluginSelections, qtpathsVariables.value(QStringLiteral(
"QT_INSTALL_PLUGINS")),
1739 libraryLocation, infix, debugMatchMode, options.platform, &platformPlugin,
1740 options.deployInsightTrackerPlugin, deployOpenSslPlugin);
1743 QString qtGuiLibrary;
1744 for (
const auto &qtModule : qtModuleEntries) {
1745 if (result.deployedQtLibraries.test(qtModule.id)) {
1746 const QString library = libraryPath(libraryLocation, qtModule.name.toUtf8(), infix,
1747 options.platform, result.isDebug);
1748 deployedQtLibraries.append(library);
1749 if (qtModule.id == QtGuiModuleId)
1750 qtGuiLibrary = library;
1755 std::wcout <<
"Direct dependencies: " << formatQtModules(result.directlyUsedQtLibraries).constData()
1756 <<
"\nAll dependencies : " << formatQtModules(result.usedQtLibraries).constData()
1757 <<
"\nTo be deployed : " << formatQtModules(result.deployedQtLibraries).constData() <<
'\n';
1761 std::wcout <<
"Plugins: " << plugins.join(u',') <<
'\n';
1763 if (result.deployedQtLibraries.test(QtGuiModuleId) && platformPlugin.isEmpty()) {
1764 *errorMessage =QStringLiteral(
"Unable to find the platform plugin.");
1768 if (options.platform.testFlag(
WindowsBased) && !qtGuiLibrary.isEmpty()) {
1769 const QStringList guiLibraries = findDependentLibraries(qtGuiLibrary, errorMessage);
1770 const bool dependsOnOpenGl = !guiLibraries.filter(QStringLiteral(
"opengl32"), Qt::CaseInsensitive).isEmpty();
1772 const QFileInfo softwareRasterizer(qtBinDir + slash + QStringLiteral(
"opengl32sw") + QLatin1StringView(
windowsSharedLibrarySuffix));
1773 if (softwareRasterizer.isFile())
1774 deployedQtLibraries.append(softwareRasterizer.absoluteFilePath());
1777 const QString d3dCompiler = findD3dCompiler(options.platform, qtBinDir,
1778 peHeaderInfo.wordSize);
1779 if (d3dCompiler.isEmpty()) {
1780 std::wcerr <<
"Warning: Cannot find any version of the d3dcompiler DLL.\n";
1782 deployedQtLibraries.push_back(d3dCompiler);
1786 const QStringList dxcLibs = findDxc(options.platform, qtBinDir,
1787 peHeaderInfo.wordSize);
1788 if (!dxcLibs.isEmpty())
1789 deployedQtLibraries.append(dxcLibs);
1791 std::wcerr <<
"Warning: Cannot find any version of the dxcompiler.dll and dxil.dll.\n";
1797 && !plugins.filter(QStringLiteral(
"ffmpegmediaplugin"), Qt::CaseInsensitive).empty()) {
1798 deployedQtLibraries.append(findFFmpegLibs(qtBinDir, options.platform));
1803 const QString targetPath = options.libraryDirectory.isEmpty() ?
1804 options.directory : options.libraryDirectory;
1805 QStringList libraries = deployedQtLibraries;
1807 libraries.append(compilerRunTimeLibs(qtBinDir, options.platform, result.isDebug,
1808 peHeaderInfo.machineArch));
1810 for (
const QString &qtLib : std::as_const(libraries)) {
1811 if (isSystemLibrary(qtLib)) {
1812 std::wcout <<
"Skipping system library " << qtLib <<
"\n";
1815 if (!updateLibrary(qtLib, targetPath, options, errorMessage))
1819#if !QT_CONFIG(relocatable)
1820 if (options.patchQt && !options.dryRun) {
1821 const QString qt6CoreName = QFileInfo(libraryPath(libraryLocation,
"Qt6Core", infix,
1822 options.platform, result.isDebug)).fileName();
1823 if (!patchQtCore(targetPath + u'/' + qt6CoreName, errorMessage)) {
1824 std::wcerr <<
"Warning: " << *errorMessage <<
'\n';
1825 errorMessage->clear();
1833 const QString targetPath = options.pluginDirectory.isEmpty() ?
1834 options.directory : options.pluginDirectory;
1835 QDir dir(targetPath);
1836 if (!dir.exists() && !dir.mkpath(QStringLiteral(
"."))) {
1837 *errorMessage =
"Cannot create "_L1 +
1838 QDir::toNativeSeparators(dir.absolutePath()) + u'.';
1841 for (
const QString &plugin : plugins) {
1842 const QString targetDirName = plugin.section(slash, -2, -2);
1843 const QString targetPath = dir.absoluteFilePath(targetDirName);
1844 if (!dir.exists(targetDirName)) {
1845 if (optVerboseLevel)
1846 std::wcout <<
"Creating directory " << targetPath <<
".\n";
1847 if (!(options.updateFileFlags & SkipUpdateFile) && !dir.mkdir(targetDirName)) {
1848 *errorMessage = QStringLiteral(
"Cannot create ") + targetDirName + u'.';
1852 if (!updateLibrary(plugin, targetPath, options, errorMessage))
1861 const QString targetPath = options.qmlDirectory.isEmpty()
1862 ? options.directory + QStringLiteral(
"/qml")
1863 : options.qmlDirectory;
1864 if (!createDirectory(targetPath, errorMessage, options.dryRun))
1866 for (
const QmlImportScanResult::Module &module : std::as_const(qmlScanResult.modules)) {
1867 const QString installPath = module.installPath(targetPath);
1868 if (optVerboseLevel > 1)
1869 std::wcout <<
"Installing: '" << module.name
1870 <<
"' from " << module.sourcePath <<
" to "
1871 << QDir::toNativeSeparators(installPath) <<
'\n';
1872 if (installPath != targetPath && !createDirectory(installPath, errorMessage, options.dryRun))
1874 unsigned updateFileFlags = options.updateFileFlags
1875 | SkipQmlDesignerSpecificsDirectories;
1876 unsigned qmlDirectoryFileFlags = 0;
1877 if (options.deployPdb)
1878 qmlDirectoryFileFlags |= QmlDirectoryFileEntryFunction::DeployPdb;
1879 if (!updateFile(module.sourcePath, QmlDirectoryFileEntryFunction(module.sourcePath,
1882 qmlDirectoryFileFlags),
1883 installPath, updateFileFlags, options.json, errorMessage)) {
1890 if (!createDirectory(options.translationsDirectory, errorMessage, options.dryRun))
1892 if (!deployTranslations(qtpathsVariables.value(QStringLiteral(
"QT_INSTALL_TRANSLATIONS")),
1893 result.deployedQtLibraries, options.translationsDirectory, options,
1926 bool isDebug, QString *errorMessage)
1928 static const char *installDataFilesRelease[] = {
1929 "icudtl.dat",
"qtwebengine_devtools_resources.pak",
"qtwebengine_resources.pak",
1930 "qtwebengine_resources_100p.pak",
"qtwebengine_resources_200p.pak"
1932 static const char *installDataFilesDebug[] = {
1933 "icudtl.dat",
"qtwebengine_devtools_resources.debug.pak",
"qtwebengine_resources.debug.pak",
1934 "qtwebengine_resources_100p.debug.pak",
"qtwebengine_resources_200p.debug.pak"
1936 static const auto &installDataFiles = isDebug ? installDataFilesDebug : installDataFilesRelease;
1937 static const auto installV8SnapshotFile =
1938 isDebug ?
"v8_context_snapshot.debug.bin" :
"v8_context_snapshot.bin";
1941 if (isDebug && platformHasDebugSuffix(options.platform))
1942 webEngineProcessName.append(
'd');
1944 std::wcout <<
"Deploying: " << webEngineProcessName.constData() <<
"...\n";
1945 if (!deployWebProcess(qtpathsVariables, webEngineProcessName, pluginInfo, options, errorMessage))
1947 const QString resourcesSubDir = QStringLiteral(
"/resources");
1948 const QString resourcesSourceDir = qtpathsVariables.value(QStringLiteral(
"QT_INSTALL_DATA"))
1949 + resourcesSubDir + u'/';
1950 const QString resourcesTargetDir(options.directory + resourcesSubDir);
1951 if (!createDirectory(resourcesTargetDir, errorMessage, options.dryRun))
1953 for (
auto installDataFile : installDataFiles) {
1954 if (!updateFile(resourcesSourceDir + QLatin1StringView(installDataFile),
1955 resourcesTargetDir, options.updateFileFlags, options.json, errorMessage)) {
1960 updateFile(resourcesSourceDir + QLatin1StringView(installV8SnapshotFile), resourcesTargetDir,
1962 errorMessage->clear();
1963 const QFileInfo translations(qtpathsVariables.value(QStringLiteral(
"QT_INSTALL_TRANSLATIONS"))
1964 + QStringLiteral(
"/qtwebengine_locales"));
1965 if (!translations.isDir()) {
1966 std::wcerr <<
"Warning: Cannot find the translation files of the QtWebEngine module at "
1967 << QDir::toNativeSeparators(translations.absoluteFilePath()) <<
".\n";
1972 return createDirectory(options.translationsDirectory, errorMessage, options.dryRun)
1973 && updateFile(translations.absoluteFilePath(), options.translationsDirectory,
1974 options.updateFileFlags, options.json, errorMessage);
1977 const QFileInfo enUSpak(translations.filePath() + QStringLiteral(
"/en-US.pak"));
1978 if (!enUSpak.exists()) {
1979 std::wcerr <<
"Warning: Cannot find "
1980 << QDir::toNativeSeparators(enUSpak.absoluteFilePath()) <<
".\n";
1983 const QString webEngineTranslationsDir = options.translationsDirectory + u'/'
1984 + translations.fileName();
1985 if (!createDirectory(webEngineTranslationsDir, errorMessage, options.dryRun))
1987 return updateFile(enUSpak.absoluteFilePath(), webEngineTranslationsDir,
1988 options.updateFileFlags, options.json, errorMessage);
1997 QCoreApplication a(argc, argv);
1998 QCoreApplication::setApplicationVersion(QT_VERSION_STR
""_L1);
2000 const QByteArray qtBinPath = QFile::encodeName(QDir::toNativeSeparators(QCoreApplication::applicationDirPath()));
2001 QByteArray path = qgetenv(
"PATH");
2002 if (!path.contains(qtBinPath)) {
2003 path.prepend(QDir::listSeparator().toLatin1());
2004 path.prepend(qtBinPath);
2005 qputenv(
"PATH", path);
2009 QString errorMessage;
2014 int result = parseEarlyArguments(QCoreApplication::arguments(), &options, &errorMessage);
2016 std::wcerr <<
"Error: " << errorMessage <<
"\n";
2021 const QMap<QString, QString> qtpathsVariables =
2022 queryQtPaths(options.qtpathsBinary, &errorMessage);
2023 const QString xSpec = qtpathsVariables.value(QStringLiteral(
"QMAKE_XSPEC"));
2024 if (qtpathsVariables.isEmpty() || xSpec.isEmpty()
2025 || !qtpathsVariables.contains(QStringLiteral(
"QT_INSTALL_BINS"))) {
2026 std::wcerr <<
"Unable to query qtpaths: " << errorMessage <<
'\n';
2030 options.platform = platformFromMkSpec(xSpec);
2035 switch (si.wProcessorArchitecture) {
2036 case PROCESSOR_ARCHITECTURE_INTEL:
2037 case PROCESSOR_ARCHITECTURE_IA64:
2038 case PROCESSOR_ARCHITECTURE_AMD64:
2041 case PROCESSOR_ARCHITECTURE_ARM:
2042 case PROCESSOR_ARCHITECTURE_ARM64:
2050 std::wcerr <<
"Unsupported platform " << xSpec <<
'\n';
2055 const QString modulesDir
2056 = qtpathsVariables.value(QLatin1String(
"QT_INSTALL_ARCHDATA"))
2057 + QLatin1String(
"/modules");
2058 const QString translationsDir
2059 = qtpathsVariables.value(QLatin1String(
"QT_INSTALL_TRANSLATIONS"));
2062 std::wcerr <<
"Error: " << errorMessage <<
"\n";
2069 pluginInfo.generateAvailablePlugins(qtpathsVariables, options.platform);
2073 QCommandLineParser parser;
2074 QString errorMessage;
2075 const int result = parseArguments(QCoreApplication::arguments(), &parser, &options, &errorMessage);
2077 std::wcerr << errorMessage <<
"\n\n";
2079 std::fputs(QT_VERSION_STR
"\n", stdout);
2083 std::fputs(qPrintable(helpText(parser, pluginInfo)), stdout);
2091 if (!createDirectory(options.directory, &errorMessage, options.dryRun)) {
2092 std::wcerr << errorMessage <<
'\n';
2095 if (!options.libraryDirectory.isEmpty() && options.libraryDirectory != options.directory
2096 && !createDirectory(options.libraryDirectory, &errorMessage, options.dryRun)) {
2097 std::wcerr << errorMessage <<
'\n';
2101 const DeployResult result = deploy(options, qtpathsVariables, pluginInfo, &errorMessage);
2103 std::wcerr << errorMessage <<
'\n';
2107 if (result.deployedQtLibraries.test(QtWebEngineCoreModuleId)) {
2108 if (!deployWebEngineCore(qtpathsVariables, pluginInfo, options, result.isDebug,
2110 std::wcerr << errorMessage <<
'\n';
2115 if (options
.createAppx && !options.appxCertificatePath.isEmpty()) {
2116 const QFileInfo storeLogo(options.directory + QStringLiteral(
"/Assets/StoreLogo.png"));
2117 if (!storeLogo.exists()) {
2118 std::wcerr <<
"Error: Could not open application logo file " << storeLogo.absoluteFilePath() <<
'\n';
2122 QFile certFile(options.appxCertificatePath);
2123 if (!certFile.open(QIODevice::ReadOnly)) {
2124 std::wcerr <<
"Could not open certificate file" <<
'\n';
2128 QSslCertificate cert(&certFile, QSsl::Der);
2129 QString publisher = cert.subjectDisplayName();
2131 const QString applicationName = QFileInfo(options.binaries.first()).baseName();
2132 const QString platform = options.platform.testFlag(
PlatformFlag::IntelBased) ? QStringLiteral(
"x64") : QStringLiteral(
"arm64");
2133 const QString appxFilePath(options.directory + QStringLiteral(
"/") + QStringLiteral(
"AppxManifest.xml"));
2134 QFile f(appxFilePath);
2135 if (!f.open(QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text)) {
2136 std::wcerr <<
"Could not create AppxManifest.xml" <<
'\n';
2140 QXmlStreamWriter manifestWriter(&f);
2141 manifestWriter.setAutoFormatting(
true);
2142 manifestWriter.writeStartDocument();
2143 manifestWriter.writeStartElement(QStringLiteral(
"Package"));
2144 manifestWriter.writeAttribute(QStringLiteral(
"xmlns"), QStringLiteral(
"http://schemas.microsoft.com/appx/manifest/foundation/windows10"));
2145 manifestWriter.writeAttribute(QStringLiteral(
"xmlns:uap"), QStringLiteral(
"http://schemas.microsoft.com/appx/manifest/uap/windows10"));
2146 manifestWriter.writeAttribute(QStringLiteral(
"xmlns:rescap"), QStringLiteral(
"http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"));
2148 manifestWriter.writeStartElement(QStringLiteral(
"Identity"));
2149 manifestWriter.writeAttribute(
"Name", QUuid::createUuid().toString(QUuid::WithoutBraces));
2150 manifestWriter.writeAttribute(
"Publisher", QStringLiteral(
"CN=") + publisher);
2151 manifestWriter.writeAttribute(
"Version",
"1.0.0.0");
2152 manifestWriter.writeAttribute(
"ProcessorArchitecture", platform);
2153 manifestWriter.writeEndElement();
2155 manifestWriter.writeStartElement(
"Properties");
2156 manifestWriter.writeStartElement(
"DisplayName");
2157 manifestWriter.writeCharacters(applicationName);
2158 manifestWriter.writeEndElement();
2159 manifestWriter.writeStartElement(
"PublisherDisplayName");
2160 manifestWriter.writeCharacters(publisher);
2161 manifestWriter.writeEndElement();
2162 manifestWriter.writeStartElement(
"Logo");
2163 manifestWriter.writeCharacters(
"Assets/StoreLogo.png");
2164 manifestWriter.writeEndElement();
2165 manifestWriter.writeEndElement();
2167 manifestWriter.writeStartElement(
"Dependencies");
2168 manifestWriter.writeStartElement(
"TargetDeviceFamily");
2169 manifestWriter.writeAttribute(
"Name",
"Windows.Desktop");
2170 manifestWriter.writeAttribute(
"MinVersion",
"10.0.14316.0");
2171 manifestWriter.writeAttribute(
"MaxVersionTested",
"10.0.14316.0");
2172 manifestWriter.writeEndElement();
2173 manifestWriter.writeEndElement();
2175 manifestWriter.writeStartElement(
"Capabilities");
2176 manifestWriter.writeStartElement(
"rescap:Capability");
2177 manifestWriter.writeAttribute(
"Name",
"runFullTrust");
2178 manifestWriter.writeEndElement();
2179 manifestWriter.writeEndElement();
2181 manifestWriter.writeStartElement(
"Resources");
2182 manifestWriter.writeStartElement(
"Resource");
2183 if (options.languages.isEmpty()) {
2184 QLocale locale = QLocale::system();
2185 manifestWriter.writeAttribute(
"Language", locale.bcp47Name());
2187 for (
const auto& language : options.languages) {
2188 manifestWriter.writeAttribute(
"Language", language);
2191 manifestWriter.writeEndElement();
2192 manifestWriter.writeEndElement();
2194 manifestWriter.writeStartElement(
"Applications");
2195 for (
const auto& binary : options.binaries) {
2196 const QString binaryRelative = binary.split(QStringLiteral(
"/")).last();
2197 const QString displayName = binaryRelative.split(QStringLiteral(
".")).first();
2198 QFile descriptionFile(options.directory + QStringLiteral(
"/") + QStringLiteral(
"Assets/Description_") + displayName + QStringLiteral(
".txt"));
2199 QString description;
2200 if (!descriptionFile.exists())
2201 std::wcerr <<
"Warning: No package description was provided " << descriptionFile.fileName() <<
'\n';
2202 if (descriptionFile.open(QIODevice::ReadOnly | QIODevice::Text))
2203 description = QString::fromUtf8(descriptionFile.readAll());
2205 manifestWriter.writeStartElement(
"Application");
2206 manifestWriter.writeAttribute(
"Id", displayName);
2207 manifestWriter.writeAttribute(
"Executable", binaryRelative);
2208 manifestWriter.writeAttribute(
"EntryPoint",
"Windows.FullTrustApplication");
2209 manifestWriter.writeStartElement(
"uap:VisualElements");
2210 manifestWriter.writeAttribute(
"DisplayName", displayName);
2211 manifestWriter.writeAttribute(
"Description", description);
2212 manifestWriter.writeAttribute(
"BackgroundColor",
"transparent");
2213 manifestWriter.writeAttribute(
"Square150x150Logo",
"Assets/Logo.png");
2214 manifestWriter.writeAttribute(
"Square44x44Logo",
"Assets/SmallLogo.png");
2215 manifestWriter.writeStartElement(
"uap:DefaultTile");
2216 manifestWriter.writeEndElement();
2217 manifestWriter.writeEndElement();
2218 manifestWriter.writeEndElement();
2220 manifestWriter.writeEndElement();
2221 manifestWriter.writeEndElement();
2222 manifestWriter.writeEndDocument();
2227 std::fputs(options
.json->toList(options.list, options.directory).constData(), stdout);
2229 std::fputs(options
.json->toJson().constData(), stdout);
2230 delete options
.json;
2231 options
.json =
nullptr;