diff --git a/src/tools/windeployqt/main.cpp b/src/tools/windeployqt/main.cpp index 80b6df5a065..d96f4cd82d4 100644 --- a/src/tools/windeployqt/main.cpp +++ b/src/tools/windeployqt/main.cpp @@ -182,7 +182,7 @@ struct Options { bool compilerRunTime = false; bool softwareRasterizer = true; bool ffmpeg = true; - PluginLists pluginSelections; + PluginSelections pluginSelections; Platform platform = WindowsDesktopMsvc; ModuleBitset additionalLibraries; ModuleBitset disabledLibraries; @@ -399,6 +399,10 @@ static inline int parseArguments(const QStringList &arguments, QCommandLineParse QStringLiteral("Skip plugin deployment.")); parser->addOption(noPluginsOption); + QCommandLineOption includeSoftPluginsOption(QStringLiteral("include-soft-plugins"), + QStringLiteral("Include in the deployment all relevant plugins by taking into account all soft dependencies.")); + parser->addOption(includeSoftPluginsOption); + QCommandLineOption skipPluginTypesOption(QStringLiteral("skip-plugin-types"), QStringLiteral("A comma-separated list of plugin types that are not deployed (qmltooling,generic)."), QStringLiteral("plugin types")); @@ -559,6 +563,8 @@ static inline int parseArguments(const QStringList &arguments, QCommandLineParse return CommandLineParseError; } + options->pluginSelections.includeSoftPlugins = parser->isSet(includeSoftPluginsOption); + if (parser->isSet(skipPluginTypesOption)) options->pluginSelections.disabledPluginTypes = parser->value(skipPluginTypesOption).split(u','); @@ -930,9 +936,9 @@ static qint64 qtModule(QString module, const QString &infix) // Return the path if a plugin is to be deployed static QString deployPlugin(const QString &plugin, const QDir &subDir, const bool dueToModule, - const DebugMatchMode &debugMatchMode, ModuleBitset *usedQtModules, + const DebugMatchMode &debugMatchMode, ModuleBitset *pluginNeededQtModules, const ModuleBitset &disabledQtModules, - const PluginLists &pluginSelections, const QString &libraryLocation, + const PluginSelections &pluginSelections, const QString &libraryLocation, const QString &infix, Platform platform, bool deployInsightTrackerPlugin) { @@ -986,44 +992,35 @@ static QString deployPlugin(const QString &plugin, const QDir &subDir, const boo return pluginPath; QStringList dependentQtLibs; - ModuleBitset neededModules; QString errorMessage; if (findDependentQtLibraries(libraryLocation, pluginPath, platform, &errorMessage, &dependentQtLibs)) { for (int d = 0; d < dependentQtLibs.size(); ++d) { const qint64 module = qtModule(dependentQtLibs.at(d), infix); if (module >= 0) - neededModules[module] = 1; + (*pluginNeededQtModules)[module] = 1; } } else { std::wcerr << "Warning: Cannot determine dependencies of " << QDir::toNativeSeparators(pluginPath) << ": " << errorMessage << '\n'; } - ModuleBitset missingModules; - missingModules = neededModules & disabledQtModules; - if (missingModules.any()) { + ModuleBitset disabledNeededQtModules; + disabledNeededQtModules = *pluginNeededQtModules & disabledQtModules; + if (disabledNeededQtModules.any()) { if (optVerboseLevel) { std::wcout << "Skipping plugin " << plugin << " due to disabled dependencies (" - << formatQtModules(missingModules).constData() << ").\n"; + << formatQtModules(disabledNeededQtModules).constData() << ").\n"; } return {}; } - missingModules = (neededModules & ~*usedQtModules); - if (missingModules.any()) { - *usedQtModules |= missingModules; - if (optVerboseLevel) { - std::wcout << "Adding " << formatQtModules(missingModules).constData() - << " for " << plugin << " from plugin type: " << subDirName << '\n'; - } - } return pluginPath; } static bool needsPluginType(const QString &subDirName, const PluginInformation &pluginInfo, - const PluginLists &pluginSelections) + const PluginSelections &pluginSelections) { bool needsTypeForPlugin = false; for (const QString &plugin: pluginSelections.includedPlugins) { @@ -1034,7 +1031,7 @@ static bool needsPluginType(const QString &subDirName, const PluginInformation & } QStringList findQtPlugins(ModuleBitset *usedQtModules, const ModuleBitset &disabledQtModules, - const PluginInformation &pluginInfo, const PluginLists &pluginSelections, + const PluginInformation &pluginInfo, const PluginSelections &pluginSelections, const QString &qtPluginsDirName, const QString &libraryLocation, const QString &infix, DebugMatchMode debugMatchModeIn, Platform platform, QString *platformPlugin, bool deployInsightTrackerPlugin) @@ -1043,6 +1040,7 @@ QStringList findQtPlugins(ModuleBitset *usedQtModules, const ModuleBitset &disab return QStringList(); QDir pluginsDir(qtPluginsDirName); QStringList result; + bool missingQtModulesAdded = false; const QFileInfoList &pluginDirs = pluginsDir.entryInfoList(QStringList(u"*"_s), QDir::Dirs | QDir::NoDotAndDotDot); for (const QFileInfo &subDirFi : pluginDirs) { const QString subDirName = subDirFi.fileName(); @@ -1065,18 +1063,42 @@ QStringList findQtPlugins(ModuleBitset *usedQtModules, const ModuleBitset &disab const QStringList plugins = findSharedLibraries(subDir, platform, debugMatchMode, QString()); for (const QString &plugin : plugins) { + ModuleBitset pluginNeededQtModules; const QString pluginPath = - deployPlugin(plugin, subDir, dueToModule, debugMatchMode, usedQtModules, + deployPlugin(plugin, subDir, dueToModule, debugMatchMode, &pluginNeededQtModules, disabledQtModules, pluginSelections, libraryLocation, infix, platform, deployInsightTrackerPlugin); if (!pluginPath.isEmpty()) { if (isPlatformPlugin && plugin.startsWith(u"qwindows")) *platformPlugin = subDir.absoluteFilePath(plugin); result.append(pluginPath); + + const ModuleBitset missingModules = (pluginNeededQtModules & ~*usedQtModules); + if (missingModules.any()) { + *usedQtModules |= missingModules; + missingQtModulesAdded = true; + if (optVerboseLevel) { + std::wcout << "Adding " << formatQtModules(missingModules).constData() + << " for " << plugin << " from plugin type: " << subDirName << '\n'; + } + } } } // for filter } // type matches } // for plugin folder + + // If missing Qt modules were added during plugin deployment make additional pass, because we may need + // additional plugins. + if (pluginSelections.includeSoftPlugins && missingQtModulesAdded) { + if (optVerboseLevel) { + std::wcout << "Performing additional pass of finding Qt plugins due to updated Qt module list: " + << formatQtModules(*usedQtModules).constData() << "\n"; + } + return findQtPlugins(usedQtModules, disabledQtModules, pluginInfo, pluginSelections, qtPluginsDirName, + libraryLocation, infix, debugMatchModeIn, platform, platformPlugin, + deployInsightTrackerPlugin); + } + return result; } diff --git a/src/tools/windeployqt/qtplugininfo.h b/src/tools/windeployqt/qtplugininfo.h index a5c773a6476..420b2b5e1aa 100644 --- a/src/tools/windeployqt/qtplugininfo.h +++ b/src/tools/windeployqt/qtplugininfo.h @@ -18,12 +18,13 @@ enum class PluginDetection DebugAndRelease }; -struct PluginLists +struct PluginSelections { QStringList disabledPluginTypes; QStringList enabledPluginTypes; QStringList excludedPlugins; QStringList includedPlugins; + bool includeSoftPlugins = false; }; class PluginInformation