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);
1498 const QChar slash = u'/';
1500 const QString qtBinDir = qtpathsVariables.value(QStringLiteral(
"QT_INSTALL_BINS"));
1501 const QString libraryLocation = qtBinDir;
1502 const QString infix = qtpathsVariables.value(QLatin1StringView(
qmakeInfixKey));
1503 const int version = qtVersion(qtpathsVariables);
1506 if (optVerboseLevel > 1)
1507 std::wcout <<
"Qt binaries in " << QDir::toNativeSeparators(qtBinDir) <<
'\n';
1509 QStringList dependentQtLibs;
1510 QStringList dependentNonQtLibs;
1511 PeHeaderInfoStruct peHeaderInfo;
1513 if (!readPeExecutableInfo(options.binaries.first(), errorMessage, &peHeaderInfo))
1515 if (!findDependentQtLibraries(libraryLocation, options.binaries.first(), options.platform,
1516 errorMessage, &dependentQtLibs, &dependentNonQtLibs)) {
1519 for (
int b = 1; b < options.binaries.size(); ++b) {
1520 if (!findDependentQtLibraries(libraryLocation, options.binaries.at(b), options.platform,
1521 errorMessage, &dependentQtLibs, &dependentNonQtLibs)) {
1526 const QFileInfo fi(options.binaries.first());
1527 const QString canonicalBinPath = fi.canonicalPath();
1530 for (qsizetype i = 0; i < dependentNonQtLibs.size(); ++i) {
1531 const QString nonQtLib = dependentNonQtLibs.at(i);
1532 const QString path = canonicalBinPath + u'/' + nonQtLib;
1533 if (!QFileInfo::exists(path))
1537 std::wcout <<
"Adding local dependency" << path <<
'\n';
1539 if (!findDependentQtLibraries(libraryLocation, path, options.platform,
1540 errorMessage, &dependentQtLibs, &dependentNonQtLibs)) {
1552 if (options.platform.testFlag(
Msvc) || options.platform.testFlag(
ClangMsvc)) {
1553 result
.isDebug = peHeaderInfo.isDebug;
1568 for (
int m = 0; m < dependentQtLibs.size(); ++m) {
1569 const qint64 module = qtModule(dependentQtLibs.at(m), infix);
1571 result.directlyUsedQtLibraries[module] = 1;
1574 const bool usesQml = result.directlyUsedQtLibraries.test(QtQmlModuleId);
1575 const bool usesQuick = result.directlyUsedQtLibraries.test(QtQuickModuleId);
1576 const bool uses3DQuick = result.directlyUsedQtLibraries.test(Qt3DQuickModuleId);
1577 const bool usesQml2 = !(options.disabledLibraries.test(QtQmlModuleId))
1578 && (usesQml || usesQuick || uses3DQuick || (options.additionalLibraries.test(QtQmlModuleId)));
1581 std::wcout << QDir::toNativeSeparators(options.binaries.first()) <<
' '
1582 << peHeaderInfo.wordSize <<
" bit, " << (result.isDebug ?
"debug" :
"release")
1585 std::wcout <<
" [QML]";
1589 if (dependentQtLibs.isEmpty()) {
1590 *errorMessage = QDir::toNativeSeparators(options.binaries.first()) + QStringLiteral(
" does not seem to be a Qt executable.");
1598 QStringList qmlImportPaths = options.qmlImportPaths;
1600 qmlImportPaths << qtpathsVariables.value(QStringLiteral(
"QT_INSTALL_QML"));
1601 QStringList qmlDirectories = options.qmlDirectories;
1602 if (qmlDirectories.isEmpty()) {
1603 const QString qmlDirectory = findQmlDirectory(options.platform, options.directory);
1604 if (!qmlDirectory.isEmpty())
1605 qmlDirectories.append(qmlDirectory);
1607 for (
const QString &qmlDirectory : std::as_const(qmlDirectories)) {
1608 if (optVerboseLevel >= 1)
1609 std::wcout <<
"Scanning " << QDir::toNativeSeparators(qmlDirectory) <<
":\n";
1610 const QmlImportScanResult scanResult =
1611 runQmlImportScanner(qmlDirectory, qmlImportPaths,
1612 result.directlyUsedQtLibraries.test(QtWidgetsModuleId),
1613 options.platform, debugMatchMode, errorMessage,
1614 options.qmlImportTimeout);
1617 qmlScanResult.append(scanResult);
1619 for (
const QString &plugin : std::as_const(qmlScanResult.plugins)) {
1620 if (!findDependentQtLibraries(libraryLocation, plugin, options.platform,
1621 errorMessage, &dependentQtLibs,
nullptr)) {
1625 if (optVerboseLevel >= 1) {
1626 std::wcout <<
"QML imports:\n";
1627 for (
const QmlImportScanResult::Module &mod : std::as_const(qmlScanResult.modules)) {
1628 std::wcout <<
" '" << mod.name <<
"' "
1629 << QDir::toNativeSeparators(mod.sourcePath) <<
'\n';
1631 if (optVerboseLevel >= 2) {
1632 std::wcout <<
"QML plugins:\n";
1633 for (
const QString &p : std::as_const(qmlScanResult.plugins))
1634 std::wcout <<
" " << QDir::toNativeSeparators(p) <<
'\n';
1640 QString platformPlugin;
1643 QStringList deployedQtLibraries;
1644 for (
int i = 0 ; i < dependentQtLibs.size(); ++i) {
1645 const qint64 module = qtModule(dependentQtLibs.at(i), infix);
1647 result.usedQtLibraries[module] = 1;
1649 deployedQtLibraries.push_back(dependentQtLibs.at(i));
1651 result.deployedQtLibraries = (result.usedQtLibraries | options.additionalLibraries) & ~options.disabledLibraries;
1653 ModuleBitset disabled = options.disabledLibraries;
1655 disabled[QtQmlModuleId] = 1;
1656 disabled[QtQuickModuleId] = 1;
1662 const QStringList qtLibs = dependentQtLibs.filter(QStringLiteral(
"Qt6Core"), Qt::CaseInsensitive)
1663 + dependentQtLibs.filter(QStringLiteral(
"Qt5WebKit"), Qt::CaseInsensitive);
1664 for (
const QString &qtLib : qtLibs) {
1665 QStringList icuLibs = findDependentLibraries(qtLib, errorMessage).filter(QStringLiteral(
"ICU"), Qt::CaseInsensitive);
1666 if (!icuLibs.isEmpty()) {
1669 const QString icuVersion = getIcuVersion(icuLibs.constFirst());
1670 if (!icuVersion.isEmpty()) {
1671 if (optVerboseLevel > 1)
1672 std::wcout <<
"Adding ICU version " << icuVersion <<
'\n';
1673 QString icuLib = QStringLiteral(
"icudt") + icuVersion
1674 + QLatin1StringView(windowsSharedLibrarySuffix);
1677 if (result.isDebug) {
1678 const QString icuLibCandidate = QStringLiteral(
"icudtd") + icuVersion
1679 + QLatin1StringView(windowsSharedLibrarySuffix);
1680 if (!findInPath(icuLibCandidate).isEmpty()) {
1681 icuLib = icuLibCandidate;
1684 icuLibs.push_back(icuLib);
1686 for (
const QString &icuLib : std::as_const(icuLibs)) {
1687 const QString icuPath = findInPath(icuLib);
1688 if (icuPath.isEmpty()) {
1689 *errorMessage = QStringLiteral(
"Unable to locate ICU library ") + icuLib;
1692 deployedQtLibraries.push_back(icuPath);
1699 QStringList openSslLibs;
1700 if (!options.openSslRootDirectory.isEmpty()) {
1701 openSslLibs = findOpenSslLibraries(options.openSslRootDirectory, options.platform);
1702 if (openSslLibs.isEmpty()) {
1703 *errorMessage = QStringLiteral(
"Unable to find openSSL libraries in ")
1704 + options.openSslRootDirectory;
1708 deployedQtLibraries.append(openSslLibs);
1712 const QStringList plugins = findQtPlugins(
1713 &result.deployedQtLibraries,
1716 disabled, pluginInfo,
1717 options.pluginSelections, qtpathsVariables.value(QStringLiteral(
"QT_INSTALL_PLUGINS")),
1718 libraryLocation, infix, debugMatchMode, options.platform, &platformPlugin,
1719 options.deployInsightTrackerPlugin, deployOpenSslPlugin);
1722 QString qtGuiLibrary;
1723 for (
const auto &qtModule : qtModuleEntries) {
1724 if (result.deployedQtLibraries.test(qtModule.id)) {
1725 const QString library = libraryPath(libraryLocation, qtModule.name.toUtf8(), infix,
1726 options.platform, result.isDebug);
1727 deployedQtLibraries.append(library);
1728 if (qtModule.id == QtGuiModuleId)
1729 qtGuiLibrary = library;
1734 std::wcout <<
"Direct dependencies: " << formatQtModules(result.directlyUsedQtLibraries).constData()
1735 <<
"\nAll dependencies : " << formatQtModules(result.usedQtLibraries).constData()
1736 <<
"\nTo be deployed : " << formatQtModules(result.deployedQtLibraries).constData() <<
'\n';
1740 std::wcout <<
"Plugins: " << plugins.join(u',') <<
'\n';
1742 if (result.deployedQtLibraries.test(QtGuiModuleId) && platformPlugin.isEmpty()) {
1743 *errorMessage =QStringLiteral(
"Unable to find the platform plugin.");
1747 if (options.platform.testFlag(
WindowsBased) && !qtGuiLibrary.isEmpty()) {
1748 const QStringList guiLibraries = findDependentLibraries(qtGuiLibrary, errorMessage);
1749 const bool dependsOnOpenGl = !guiLibraries.filter(QStringLiteral(
"opengl32"), Qt::CaseInsensitive).isEmpty();
1751 const QFileInfo softwareRasterizer(qtBinDir + slash + QStringLiteral(
"opengl32sw") + QLatin1StringView(
windowsSharedLibrarySuffix));
1752 if (softwareRasterizer.isFile())
1753 deployedQtLibraries.append(softwareRasterizer.absoluteFilePath());
1756 const QString d3dCompiler = findD3dCompiler(options.platform, qtBinDir,
1757 peHeaderInfo.wordSize);
1758 if (d3dCompiler.isEmpty()) {
1759 std::wcerr <<
"Warning: Cannot find any version of the d3dcompiler DLL.\n";
1761 deployedQtLibraries.push_back(d3dCompiler);
1765 const QStringList dxcLibs = findDxc(options.platform, qtBinDir,
1766 peHeaderInfo.wordSize);
1767 if (!dxcLibs.isEmpty())
1768 deployedQtLibraries.append(dxcLibs);
1770 std::wcerr <<
"Warning: Cannot find any version of the dxcompiler.dll and dxil.dll.\n";
1776 && !plugins.filter(QStringLiteral(
"ffmpegmediaplugin"), Qt::CaseInsensitive).empty()) {
1777 deployedQtLibraries.append(findFFmpegLibs(qtBinDir, options.platform));
1782 const QString targetPath = options.libraryDirectory.isEmpty() ?
1783 options.directory : options.libraryDirectory;
1784 QStringList libraries = deployedQtLibraries;
1786 libraries.append(compilerRunTimeLibs(qtBinDir, options.platform, result.isDebug,
1787 peHeaderInfo.machineArch));
1789 for (
const QString &qtLib : std::as_const(libraries)) {
1790 if (!updateLibrary(qtLib, targetPath, options, errorMessage))
1794#if !QT_CONFIG(relocatable)
1795 if (options.patchQt && !options.dryRun) {
1796 const QString qt6CoreName = QFileInfo(libraryPath(libraryLocation,
"Qt6Core", infix,
1797 options.platform, result.isDebug)).fileName();
1798 if (!patchQtCore(targetPath + u'/' + qt6CoreName, errorMessage)) {
1799 std::wcerr <<
"Warning: " << *errorMessage <<
'\n';
1800 errorMessage->clear();
1808 const QString targetPath = options.pluginDirectory.isEmpty() ?
1809 options.directory : options.pluginDirectory;
1810 QDir dir(targetPath);
1811 if (!dir.exists() && !dir.mkpath(QStringLiteral(
"."))) {
1812 *errorMessage =
"Cannot create "_L1 +
1813 QDir::toNativeSeparators(dir.absolutePath()) + u'.';
1816 for (
const QString &plugin : plugins) {
1817 const QString targetDirName = plugin.section(slash, -2, -2);
1818 const QString targetPath = dir.absoluteFilePath(targetDirName);
1819 if (!dir.exists(targetDirName)) {
1820 if (optVerboseLevel)
1821 std::wcout <<
"Creating directory " << targetPath <<
".\n";
1822 if (!(options.updateFileFlags & SkipUpdateFile) && !dir.mkdir(targetDirName)) {
1823 *errorMessage = QStringLiteral(
"Cannot create ") + targetDirName + u'.';
1827 if (!updateLibrary(plugin, targetPath, options, errorMessage))
1836 const QString targetPath = options.qmlDirectory.isEmpty()
1837 ? options.directory + QStringLiteral(
"/qml")
1838 : options.qmlDirectory;
1839 if (!createDirectory(targetPath, errorMessage, options.dryRun))
1841 for (
const QmlImportScanResult::Module &module : std::as_const(qmlScanResult.modules)) {
1842 const QString installPath = module.installPath(targetPath);
1843 if (optVerboseLevel > 1)
1844 std::wcout <<
"Installing: '" << module.name
1845 <<
"' from " << module.sourcePath <<
" to "
1846 << QDir::toNativeSeparators(installPath) <<
'\n';
1847 if (installPath != targetPath && !createDirectory(installPath, errorMessage, options.dryRun))
1849 unsigned updateFileFlags = options.updateFileFlags
1850 | SkipQmlDesignerSpecificsDirectories;
1851 unsigned qmlDirectoryFileFlags = 0;
1852 if (options.deployPdb)
1853 qmlDirectoryFileFlags |= QmlDirectoryFileEntryFunction::DeployPdb;
1854 if (!updateFile(module.sourcePath, QmlDirectoryFileEntryFunction(module.sourcePath,
1857 qmlDirectoryFileFlags),
1858 installPath, updateFileFlags, options.json, errorMessage)) {
1865 if (!createDirectory(options.translationsDirectory, errorMessage, options.dryRun))
1867 if (!deployTranslations(qtpathsVariables.value(QStringLiteral(
"QT_INSTALL_TRANSLATIONS")),
1868 result.deployedQtLibraries, options.translationsDirectory, options,
1901 bool isDebug, QString *errorMessage)
1903 static const char *installDataFilesRelease[] = {
1904 "icudtl.dat",
"qtwebengine_devtools_resources.pak",
"qtwebengine_resources.pak",
1905 "qtwebengine_resources_100p.pak",
"qtwebengine_resources_200p.pak"
1907 static const char *installDataFilesDebug[] = {
1908 "icudtl.dat",
"qtwebengine_devtools_resources.debug.pak",
"qtwebengine_resources.debug.pak",
1909 "qtwebengine_resources_100p.debug.pak",
"qtwebengine_resources_200p.debug.pak"
1911 static const auto &installDataFiles = isDebug ? installDataFilesDebug : installDataFilesRelease;
1912 static const auto installV8SnapshotFile =
1913 isDebug ?
"v8_context_snapshot.debug.bin" :
"v8_context_snapshot.bin";
1916 if (isDebug && platformHasDebugSuffix(options.platform))
1917 webEngineProcessName.append(
'd');
1919 std::wcout <<
"Deploying: " << webEngineProcessName.constData() <<
"...\n";
1920 if (!deployWebProcess(qtpathsVariables, webEngineProcessName, pluginInfo, options, errorMessage))
1922 const QString resourcesSubDir = QStringLiteral(
"/resources");
1923 const QString resourcesSourceDir = qtpathsVariables.value(QStringLiteral(
"QT_INSTALL_DATA"))
1924 + resourcesSubDir + u'/';
1925 const QString resourcesTargetDir(options.directory + resourcesSubDir);
1926 if (!createDirectory(resourcesTargetDir, errorMessage, options.dryRun))
1928 for (
auto installDataFile : installDataFiles) {
1929 if (!updateFile(resourcesSourceDir + QLatin1StringView(installDataFile),
1930 resourcesTargetDir, options.updateFileFlags, options.json, errorMessage)) {
1935 updateFile(resourcesSourceDir + QLatin1StringView(installV8SnapshotFile), resourcesTargetDir,
1937 errorMessage->clear();
1938 const QFileInfo translations(qtpathsVariables.value(QStringLiteral(
"QT_INSTALL_TRANSLATIONS"))
1939 + QStringLiteral(
"/qtwebengine_locales"));
1940 if (!translations.isDir()) {
1941 std::wcerr <<
"Warning: Cannot find the translation files of the QtWebEngine module at "
1942 << QDir::toNativeSeparators(translations.absoluteFilePath()) <<
".\n";
1947 return createDirectory(options.translationsDirectory, errorMessage, options.dryRun)
1948 && updateFile(translations.absoluteFilePath(), options.translationsDirectory,
1949 options.updateFileFlags, options.json, errorMessage);
1952 const QFileInfo enUSpak(translations.filePath() + QStringLiteral(
"/en-US.pak"));
1953 if (!enUSpak.exists()) {
1954 std::wcerr <<
"Warning: Cannot find "
1955 << QDir::toNativeSeparators(enUSpak.absoluteFilePath()) <<
".\n";
1958 const QString webEngineTranslationsDir = options.translationsDirectory + u'/'
1959 + translations.fileName();
1960 if (!createDirectory(webEngineTranslationsDir, errorMessage, options.dryRun))
1962 return updateFile(enUSpak.absoluteFilePath(), webEngineTranslationsDir,
1963 options.updateFileFlags, options.json, errorMessage);
1972 QCoreApplication a(argc, argv);
1973 QCoreApplication::setApplicationVersion(QT_VERSION_STR
""_L1);
1975 const QByteArray qtBinPath = QFile::encodeName(QDir::toNativeSeparators(QCoreApplication::applicationDirPath()));
1976 QByteArray path = qgetenv(
"PATH");
1977 if (!path.contains(qtBinPath)) {
1978 path.prepend(QDir::listSeparator().toLatin1());
1979 path.prepend(qtBinPath);
1980 qputenv(
"PATH", path);
1984 QString errorMessage;
1989 int result = parseEarlyArguments(QCoreApplication::arguments(), &options, &errorMessage);
1991 std::wcerr <<
"Error: " << errorMessage <<
"\n";
1996 const QMap<QString, QString> qtpathsVariables =
1997 queryQtPaths(options.qtpathsBinary, &errorMessage);
1998 const QString xSpec = qtpathsVariables.value(QStringLiteral(
"QMAKE_XSPEC"));
1999 if (qtpathsVariables.isEmpty() || xSpec.isEmpty()
2000 || !qtpathsVariables.contains(QStringLiteral(
"QT_INSTALL_BINS"))) {
2001 std::wcerr <<
"Unable to query qtpaths: " << errorMessage <<
'\n';
2005 options.platform = platformFromMkSpec(xSpec);
2010 switch (si.wProcessorArchitecture) {
2011 case PROCESSOR_ARCHITECTURE_INTEL:
2012 case PROCESSOR_ARCHITECTURE_IA64:
2013 case PROCESSOR_ARCHITECTURE_AMD64:
2016 case PROCESSOR_ARCHITECTURE_ARM:
2017 case PROCESSOR_ARCHITECTURE_ARM64:
2025 std::wcerr <<
"Unsupported platform " << xSpec <<
'\n';
2030 const QString modulesDir
2031 = qtpathsVariables.value(QLatin1String(
"QT_INSTALL_ARCHDATA"))
2032 + QLatin1String(
"/modules");
2033 const QString translationsDir
2034 = qtpathsVariables.value(QLatin1String(
"QT_INSTALL_TRANSLATIONS"));
2037 std::wcerr <<
"Error: " << errorMessage <<
"\n";
2044 pluginInfo.generateAvailablePlugins(qtpathsVariables, options.platform);
2048 QCommandLineParser parser;
2049 QString errorMessage;
2050 const int result = parseArguments(QCoreApplication::arguments(), &parser, &options, &errorMessage);
2052 std::wcerr << errorMessage <<
"\n\n";
2054 std::fputs(QT_VERSION_STR
"\n", stdout);
2057 if (result & CommandLineParseHelpRequested)
2058 std::fputs(qPrintable(helpText(parser, pluginInfo)), stdout);
2066 if (!createDirectory(options.directory, &errorMessage, options.dryRun)) {
2067 std::wcerr << errorMessage <<
'\n';
2070 if (!options.libraryDirectory.isEmpty() && options.libraryDirectory != options.directory
2071 && !createDirectory(options.libraryDirectory, &errorMessage, options.dryRun)) {
2072 std::wcerr << errorMessage <<
'\n';
2076 const DeployResult result = deploy(options, qtpathsVariables, pluginInfo, &errorMessage);
2078 std::wcerr << errorMessage <<
'\n';
2082 if (result.deployedQtLibraries.test(QtWebEngineCoreModuleId)) {
2083 if (!deployWebEngineCore(qtpathsVariables, pluginInfo, options, result.isDebug,
2085 std::wcerr << errorMessage <<
'\n';
2090 if (options
.createAppx && !options.appxCertificatePath.isEmpty()) {
2091 const QFileInfo storeLogo(options.directory + QStringLiteral(
"/Assets/StoreLogo.png"));
2092 if (!storeLogo.exists()) {
2093 std::wcerr <<
"Error: Could not open application logo file " << storeLogo.absoluteFilePath() <<
'\n';
2097 QFile certFile(options.appxCertificatePath);
2098 if (!certFile.open(QIODevice::ReadOnly)) {
2099 std::wcerr <<
"Could not open certificate file" <<
'\n';
2103 QSslCertificate cert(&certFile, QSsl::Der);
2104 QString publisher = cert.subjectDisplayName();
2106 const QString applicationName = QFileInfo(options.binaries.first()).baseName();
2107 const QString platform = options.platform.testFlag(
PlatformFlag::IntelBased) ? QStringLiteral(
"x64") : QStringLiteral(
"arm64");
2108 const QString appxFilePath(options.directory + QStringLiteral(
"/") + QStringLiteral(
"AppxManifest.xml"));
2109 QFile f(appxFilePath);
2110 if (!f.open(QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text)) {
2111 std::wcerr <<
"Could not create AppxManifest.xml" <<
'\n';
2115 QXmlStreamWriter manifestWriter(&f);
2116 manifestWriter.setAutoFormatting(
true);
2117 manifestWriter.writeStartDocument();
2118 manifestWriter.writeStartElement(QStringLiteral(
"Package"));
2119 manifestWriter.writeAttribute(QStringLiteral(
"xmlns"), QStringLiteral(
"http://schemas.microsoft.com/appx/manifest/foundation/windows10"));
2120 manifestWriter.writeAttribute(QStringLiteral(
"xmlns:uap"), QStringLiteral(
"http://schemas.microsoft.com/appx/manifest/uap/windows10"));
2121 manifestWriter.writeAttribute(QStringLiteral(
"xmlns:rescap"), QStringLiteral(
"http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"));
2123 manifestWriter.writeStartElement(QStringLiteral(
"Identity"));
2124 manifestWriter.writeAttribute(
"Name", QUuid::createUuid().toString(QUuid::WithoutBraces));
2125 manifestWriter.writeAttribute(
"Publisher", QStringLiteral(
"CN=") + publisher);
2126 manifestWriter.writeAttribute(
"Version",
"1.0.0.0");
2127 manifestWriter.writeAttribute(
"ProcessorArchitecture", platform);
2128 manifestWriter.writeEndElement();
2130 manifestWriter.writeStartElement(
"Properties");
2131 manifestWriter.writeStartElement(
"DisplayName");
2132 manifestWriter.writeCharacters(applicationName);
2133 manifestWriter.writeEndElement();
2134 manifestWriter.writeStartElement(
"PublisherDisplayName");
2135 manifestWriter.writeCharacters(publisher);
2136 manifestWriter.writeEndElement();
2137 manifestWriter.writeStartElement(
"Logo");
2138 manifestWriter.writeCharacters(
"Assets/StoreLogo.png");
2139 manifestWriter.writeEndElement();
2140 manifestWriter.writeEndElement();
2142 manifestWriter.writeStartElement(
"Dependencies");
2143 manifestWriter.writeStartElement(
"TargetDeviceFamily");
2144 manifestWriter.writeAttribute(
"Name",
"Windows.Desktop");
2145 manifestWriter.writeAttribute(
"MinVersion",
"10.0.14316.0");
2146 manifestWriter.writeAttribute(
"MaxVersionTested",
"10.0.14316.0");
2147 manifestWriter.writeEndElement();
2148 manifestWriter.writeEndElement();
2150 manifestWriter.writeStartElement(
"Capabilities");
2151 manifestWriter.writeStartElement(
"rescap:Capability");
2152 manifestWriter.writeAttribute(
"Name",
"runFullTrust");
2153 manifestWriter.writeEndElement();
2154 manifestWriter.writeEndElement();
2156 manifestWriter.writeStartElement(
"Resources");
2157 manifestWriter.writeStartElement(
"Resource");
2158 if (options.languages.isEmpty()) {
2159 QLocale locale = QLocale::system();
2160 manifestWriter.writeAttribute(
"Language", locale.bcp47Name());
2162 for (
const auto& language : options.languages) {
2163 manifestWriter.writeAttribute(
"Language", language);
2166 manifestWriter.writeEndElement();
2167 manifestWriter.writeEndElement();
2169 manifestWriter.writeStartElement(
"Applications");
2170 for (
const auto& binary : options.binaries) {
2171 const QString binaryRelative = binary.split(QStringLiteral(
"/")).last();
2172 const QString displayName = binaryRelative.split(QStringLiteral(
".")).first();
2173 QFile descriptionFile(options.directory + QStringLiteral(
"/") + QStringLiteral(
"Assets/Description_") + displayName + QStringLiteral(
".txt"));
2174 QString description;
2175 if (!descriptionFile.exists())
2176 std::wcerr <<
"Warning: No package description was provided " << descriptionFile.fileName() <<
'\n';
2177 if (descriptionFile.open(QIODevice::ReadOnly | QIODevice::Text))
2178 description = QString::fromUtf8(descriptionFile.readAll());
2180 manifestWriter.writeStartElement(
"Application");
2181 manifestWriter.writeAttribute(
"Id", displayName);
2182 manifestWriter.writeAttribute(
"Executable", binaryRelative);
2183 manifestWriter.writeAttribute(
"EntryPoint",
"Windows.FullTrustApplication");
2184 manifestWriter.writeStartElement(
"uap:VisualElements");
2185 manifestWriter.writeAttribute(
"DisplayName", displayName);
2186 manifestWriter.writeAttribute(
"Description", description);
2187 manifestWriter.writeAttribute(
"BackgroundColor",
"transparent");
2188 manifestWriter.writeAttribute(
"Square150x150Logo",
"Assets/Logo.png");
2189 manifestWriter.writeAttribute(
"Square44x44Logo",
"Assets/SmallLogo.png");
2190 manifestWriter.writeStartElement(
"uap:DefaultTile");
2191 manifestWriter.writeEndElement();
2192 manifestWriter.writeEndElement();
2193 manifestWriter.writeEndElement();
2195 manifestWriter.writeEndElement();
2196 manifestWriter.writeEndElement();
2197 manifestWriter.writeEndDocument();
2202 std::fputs(options
.json->toList(options.list, options.directory).constData(), stdout);
2204 std::fputs(options
.json->toJson().constData(), stdout);
2205 delete options
.json;
2206 options
.json =
nullptr;