From fdcebd63cd10bfc5ba02180d6ebdefbe67c40666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Policht?= Date: Sun, 28 Jan 2024 10:52:40 +0100 Subject: [PATCH] Windeployqt: introduce --include-soft-plugins command line option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some plugins may have dependencies on additional Qt modules. This is handled by deployPlugin() function. Existing code does not look for plugins associated with these modules however, because the loop iterates over plugin directories only once. This change introduces an option that will make windeployqt take into account all such soft dependencies by making recursive calls to findQtPlugins(). Task-number: QTBUG-121583 Change-Id: Id6535426a47f9b92a3035e864dfdd7577b82c9ad Reviewed-by: Oliver Wolff Reviewed-by: Timothée Keller (cherry picked from commit 5010eda5345bdbfc12e134d6fb3ae5b7370e2185) Reviewed-by: Qt Cherry-pick Bot --- src/tools/windeployqt/main.cpp | 62 +++++++++++++++++++--------- src/tools/windeployqt/qtplugininfo.h | 3 +- 2 files changed, 44 insertions(+), 21 deletions(-) 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