Windeployqt: introduce --include-soft-plugins command line option

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 <oliver.wolff@qt.io>
Reviewed-by: Timothée Keller <timothee.keller@qt.io>
(cherry picked from commit 5010eda5345bdbfc12e134d6fb3ae5b7370e2185)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Michał Policht 2024-01-28 10:52:40 +01:00 committed by Qt Cherry-pick Bot
parent acb5a72ef0
commit fdcebd63cd
2 changed files with 44 additions and 21 deletions

View File

@ -182,7 +182,7 @@ struct Options {
bool compilerRunTime = false; bool compilerRunTime = false;
bool softwareRasterizer = true; bool softwareRasterizer = true;
bool ffmpeg = true; bool ffmpeg = true;
PluginLists pluginSelections; PluginSelections pluginSelections;
Platform platform = WindowsDesktopMsvc; Platform platform = WindowsDesktopMsvc;
ModuleBitset additionalLibraries; ModuleBitset additionalLibraries;
ModuleBitset disabledLibraries; ModuleBitset disabledLibraries;
@ -399,6 +399,10 @@ static inline int parseArguments(const QStringList &arguments, QCommandLineParse
QStringLiteral("Skip plugin deployment.")); QStringLiteral("Skip plugin deployment."));
parser->addOption(noPluginsOption); 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"), QCommandLineOption skipPluginTypesOption(QStringLiteral("skip-plugin-types"),
QStringLiteral("A comma-separated list of plugin types that are not deployed (qmltooling,generic)."), QStringLiteral("A comma-separated list of plugin types that are not deployed (qmltooling,generic)."),
QStringLiteral("plugin types")); QStringLiteral("plugin types"));
@ -559,6 +563,8 @@ static inline int parseArguments(const QStringList &arguments, QCommandLineParse
return CommandLineParseError; return CommandLineParseError;
} }
options->pluginSelections.includeSoftPlugins = parser->isSet(includeSoftPluginsOption);
if (parser->isSet(skipPluginTypesOption)) if (parser->isSet(skipPluginTypesOption))
options->pluginSelections.disabledPluginTypes = parser->value(skipPluginTypesOption).split(u','); 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 // Return the path if a plugin is to be deployed
static QString deployPlugin(const QString &plugin, const QDir &subDir, const bool dueToModule, 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 ModuleBitset &disabledQtModules,
const PluginLists &pluginSelections, const QString &libraryLocation, const PluginSelections &pluginSelections, const QString &libraryLocation,
const QString &infix, Platform platform, const QString &infix, Platform platform,
bool deployInsightTrackerPlugin) bool deployInsightTrackerPlugin)
{ {
@ -986,44 +992,35 @@ static QString deployPlugin(const QString &plugin, const QDir &subDir, const boo
return pluginPath; return pluginPath;
QStringList dependentQtLibs; QStringList dependentQtLibs;
ModuleBitset neededModules;
QString errorMessage; QString errorMessage;
if (findDependentQtLibraries(libraryLocation, pluginPath, platform, if (findDependentQtLibraries(libraryLocation, pluginPath, platform,
&errorMessage, &dependentQtLibs)) { &errorMessage, &dependentQtLibs)) {
for (int d = 0; d < dependentQtLibs.size(); ++d) { for (int d = 0; d < dependentQtLibs.size(); ++d) {
const qint64 module = qtModule(dependentQtLibs.at(d), infix); const qint64 module = qtModule(dependentQtLibs.at(d), infix);
if (module >= 0) if (module >= 0)
neededModules[module] = 1; (*pluginNeededQtModules)[module] = 1;
} }
} else { } else {
std::wcerr << "Warning: Cannot determine dependencies of " std::wcerr << "Warning: Cannot determine dependencies of "
<< QDir::toNativeSeparators(pluginPath) << ": " << errorMessage << '\n'; << QDir::toNativeSeparators(pluginPath) << ": " << errorMessage << '\n';
} }
ModuleBitset missingModules; ModuleBitset disabledNeededQtModules;
missingModules = neededModules & disabledQtModules; disabledNeededQtModules = *pluginNeededQtModules & disabledQtModules;
if (missingModules.any()) { if (disabledNeededQtModules.any()) {
if (optVerboseLevel) { if (optVerboseLevel) {
std::wcout << "Skipping plugin " << plugin std::wcout << "Skipping plugin " << plugin
<< " due to disabled dependencies (" << " due to disabled dependencies ("
<< formatQtModules(missingModules).constData() << ").\n"; << formatQtModules(disabledNeededQtModules).constData() << ").\n";
} }
return {}; 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; return pluginPath;
} }
static bool needsPluginType(const QString &subDirName, const PluginInformation &pluginInfo, static bool needsPluginType(const QString &subDirName, const PluginInformation &pluginInfo,
const PluginLists &pluginSelections) const PluginSelections &pluginSelections)
{ {
bool needsTypeForPlugin = false; bool needsTypeForPlugin = false;
for (const QString &plugin: pluginSelections.includedPlugins) { 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, 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 &qtPluginsDirName, const QString &libraryLocation,
const QString &infix, DebugMatchMode debugMatchModeIn, Platform platform, const QString &infix, DebugMatchMode debugMatchModeIn, Platform platform,
QString *platformPlugin, bool deployInsightTrackerPlugin) QString *platformPlugin, bool deployInsightTrackerPlugin)
@ -1043,6 +1040,7 @@ QStringList findQtPlugins(ModuleBitset *usedQtModules, const ModuleBitset &disab
return QStringList(); return QStringList();
QDir pluginsDir(qtPluginsDirName); QDir pluginsDir(qtPluginsDirName);
QStringList result; QStringList result;
bool missingQtModulesAdded = false;
const QFileInfoList &pluginDirs = pluginsDir.entryInfoList(QStringList(u"*"_s), QDir::Dirs | QDir::NoDotAndDotDot); const QFileInfoList &pluginDirs = pluginsDir.entryInfoList(QStringList(u"*"_s), QDir::Dirs | QDir::NoDotAndDotDot);
for (const QFileInfo &subDirFi : pluginDirs) { for (const QFileInfo &subDirFi : pluginDirs) {
const QString subDirName = subDirFi.fileName(); const QString subDirName = subDirFi.fileName();
@ -1065,18 +1063,42 @@ QStringList findQtPlugins(ModuleBitset *usedQtModules, const ModuleBitset &disab
const QStringList plugins = const QStringList plugins =
findSharedLibraries(subDir, platform, debugMatchMode, QString()); findSharedLibraries(subDir, platform, debugMatchMode, QString());
for (const QString &plugin : plugins) { for (const QString &plugin : plugins) {
ModuleBitset pluginNeededQtModules;
const QString pluginPath = const QString pluginPath =
deployPlugin(plugin, subDir, dueToModule, debugMatchMode, usedQtModules, deployPlugin(plugin, subDir, dueToModule, debugMatchMode, &pluginNeededQtModules,
disabledQtModules, pluginSelections, libraryLocation, infix, disabledQtModules, pluginSelections, libraryLocation, infix,
platform, deployInsightTrackerPlugin); platform, deployInsightTrackerPlugin);
if (!pluginPath.isEmpty()) { if (!pluginPath.isEmpty()) {
if (isPlatformPlugin && plugin.startsWith(u"qwindows")) if (isPlatformPlugin && plugin.startsWith(u"qwindows"))
*platformPlugin = subDir.absoluteFilePath(plugin); *platformPlugin = subDir.absoluteFilePath(plugin);
result.append(pluginPath); 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 } // for filter
} // type matches } // type matches
} // for plugin folder } // 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; return result;
} }

View File

@ -18,12 +18,13 @@ enum class PluginDetection
DebugAndRelease DebugAndRelease
}; };
struct PluginLists struct PluginSelections
{ {
QStringList disabledPluginTypes; QStringList disabledPluginTypes;
QStringList enabledPluginTypes; QStringList enabledPluginTypes;
QStringList excludedPlugins; QStringList excludedPlugins;
QStringList includedPlugins; QStringList includedPlugins;
bool includeSoftPlugins = false;
}; };
class PluginInformation class PluginInformation