summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@digia.com>2013-05-24 14:57:03 +0200
committerFriedemann Kleint <Friedemann.Kleint@digia.com>2013-05-27 16:07:35 +0200
commitb208f7bd0eab5c166057e58c5f7e6ee264975432 (patch)
tree11f3b4a78ebf6b84f3356301ba886794bb880594
parent106767ca7db2172f7db0df4dc4a76eaff2b93aee (diff)
downloadqttools-b208f7bd0eab5c166057e58c5f7e6ee264975432.tar.gz
windeployqt: Exactly match debug/release DLLs based on PE information.
Change updateFile() to be a template to be able to match files with a predicate function and implement it for discriminating debug/release DLLs. Reshuffle code, move findQtPlugins() to main.cpp. Change-Id: I75fe90ed2329a482d294abf6faeac700e1234167 Reviewed-by: Oliver Wolff <oliver.wolff@digia.com> Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
-rw-r--r--src/windeployqt/main.cpp131
-rw-r--r--src/windeployqt/utils.cpp56
-rw-r--r--src/windeployqt/utils.h101
3 files changed, 214 insertions, 74 deletions
diff --git a/src/windeployqt/main.cpp b/src/windeployqt/main.cpp
index a886faa97..59dc5243d 100644
--- a/src/windeployqt/main.cpp
+++ b/src/windeployqt/main.cpp
@@ -119,10 +119,11 @@ static inline QString findD3dCompiler()
// Helper for recursively finding all dependent Qt libraries.
static bool findDependentQtLibraries(const QString &qtBinDir, const QString &binary,
- QStringList *result, QString *errorMessage)
+ QString *errorMessage, QStringList *result,
+ unsigned *wordSize = 0, bool *isDebug = 0)
{
- const QStringList dependentLibs = findDependentLibraries(binary, errorMessage);
- if (dependentLibs.isEmpty()) {
+ QStringList dependentLibs;
+ if (!readPeExecutable(binary, errorMessage, &dependentLibs, wordSize, isDebug)) {
errorMessage->prepend(QLatin1String("Unable to find dependent libraries of ") +
QDir::toNativeSeparators(binary) + QLatin1String(" :"));
return false;
@@ -134,13 +135,113 @@ static bool findDependentQtLibraries(const QString &qtBinDir, const QString &bin
const QString path = normalizeFileName(qtBinDir + QLatin1Char('/') + QFileInfo(qtLib).fileName());
if (!result->contains(path)) {
result->append(path);
- if (!findDependentQtLibraries(qtBinDir, path, result, errorMessage))
+ if (!findDependentQtLibraries(qtBinDir, path, errorMessage, result))
return false;
}
}
return true;
}
+// Base class to filter debug/release DLLs for functions to be passed to updateFile().
+// Tries to pre-filter by namefilter and does check via PE.
+class DllDirectoryFileEntryFunction {
+public:
+ explicit DllDirectoryFileEntryFunction(bool debug, const QString &prefix = QStringLiteral("*")) :
+ m_nameFilter(QStringList(prefix + (debug ? QStringLiteral("d.dll") : QStringLiteral(".dll")))),
+ m_dllDebug(debug) {}
+
+ QStringList operator()(const QDir &dir) const
+ {
+ QStringList result;
+ QString errorMessage;
+ foreach (const QString &dll, m_nameFilter(dir)) {
+ const QString dllPath = dir.absoluteFilePath(dll);
+ bool debugDll;
+ if (readPeExecutable(dllPath, &errorMessage, 0, 0, &debugDll)) {
+ if (debugDll == m_dllDebug) {
+ result.push_back(dll);
+ }
+ } else {
+ std::fprintf(stderr, "Warning: Unable to read %s: %s",
+ qPrintable(QDir::toNativeSeparators(dllPath)), qPrintable(errorMessage));
+ }
+ }
+ return result;
+ }
+
+private:
+ const NameFilterFileEntryFunction m_nameFilter;
+ const bool m_dllDebug;
+};
+
+// File entry filter function for updateFile() that returns a list of files for
+// QML import trees: DLLs (matching debgug) and .qml/,js, etc.
+class QmlDirectoryFileEntryFunction {
+public:
+ explicit QmlDirectoryFileEntryFunction(bool debug)
+ : m_qmlNameFilter(QStringList() << QStringLiteral("*.js") << QStringLiteral("qmldir") << QStringLiteral("*.qmltypes"))
+ , m_dllFilter(debug)
+ {}
+
+ QStringList operator()(const QDir &dir) const { return m_dllFilter(dir) + m_qmlNameFilter(dir); }
+
+private:
+ NameFilterFileEntryFunction m_qmlNameFilter;
+ DllDirectoryFileEntryFunction m_dllFilter;
+};
+
+static inline unsigned qtModuleForPlugin(const QString &subDirName)
+{
+ if (subDirName == QLatin1String("accessible") || subDirName == QLatin1String("iconengines")
+ || subDirName == QLatin1String("imageformats") || subDirName == QLatin1String("platforms")) {
+ return GuiModule;
+ }
+ if (subDirName == QLatin1String("bearer"))
+ return NetworkModule;
+ if (subDirName == QLatin1String("sqldrivers"))
+ return SqlModule;
+ if (subDirName == QLatin1String("mediaservice") || subDirName == QLatin1String("playlistformats"))
+ return MultimediaModule;
+ if (subDirName == QLatin1String("printsupport"))
+ return PrintSupportModule;
+ if (subDirName == QLatin1String("qmltooling"))
+ return Quick1Module | Quick2Module;
+ return 0; // "designer"
+}
+
+QStringList findQtPlugins(unsigned usedQtModules,
+ const QString qtPluginsDirName,
+ bool debug, Platform platform,
+ QString *platformPlugin)
+{
+ if (qtPluginsDirName.isEmpty())
+ return QStringList();
+ QDir pluginsDir(qtPluginsDirName);
+ QStringList result;
+ foreach (const QString &subDirName, pluginsDir.entryList(QStringList(QLatin1String("*")), QDir::Dirs | QDir::NoDotAndDotDot)) {
+ const unsigned module = qtModuleForPlugin(subDirName);
+ if (module & usedQtModules) {
+ const QString subDirPath = qtPluginsDirName + QLatin1Char('/') + subDirName;
+ QDir subDir(subDirPath);
+ // Filter for platform or any.
+ QString filter;
+ const bool isPlatformPlugin = subDirName == QLatin1String("platforms");
+ if (isPlatformPlugin) {
+ filter = platform == WinRt ? QStringLiteral("qwinrt") : QStringLiteral("qwindows");
+ } else {
+ filter = QLatin1String("*");
+ }
+ foreach (const QString &plugin, DllDirectoryFileEntryFunction(debug, filter)(subDir)) {
+ const QString pluginPath = subDir.absoluteFilePath(plugin);
+ if (isPlatformPlugin)
+ *platformPlugin = pluginPath;
+ result.push_back(pluginPath);
+ } // for filter
+ } // type matches
+ } // for plugin folder
+ return result;
+}
+
static unsigned qtModules(const QStringList &qtLibraries)
{
unsigned result = 0;
@@ -200,22 +301,25 @@ int main(int argc, char **argv)
std::fprintf(stderr, "Qt binaries in %s\n", qPrintable(QDir::toNativeSeparators(qtBinDir)));
QStringList dependentQtLibs;
- if (!findDependentQtLibraries(qtBinDir, binary, &dependentQtLibs, &errorMessage)) {
+ bool isDebug;
+ unsigned wordSize;
+ if (!findDependentQtLibraries(qtBinDir, binary, &errorMessage, &dependentQtLibs, &wordSize, &isDebug)) {
std::fputs(qPrintable(errorMessage), stderr);
return 1;
}
+ std::printf("%s: %ubit, %s executable.\n", qPrintable(QDir::toNativeSeparators(binary)),
+ wordSize, isDebug ? "debug" : "release");
+
if (dependentQtLibs.isEmpty()) {
std::fprintf(stderr, "%s does not seem to be a Qt executable\n",
qPrintable(QDir::toNativeSeparators(binary)));
return 1;
}
- // Some checks in QtCore: Debug, ICU
- bool isDebug = false;
+ // Some checks in QtCore: ICU
const QStringList qt5Core = dependentQtLibs.filter(QStringLiteral("Qt5Core"), Qt::CaseInsensitive);
if (!qt5Core.isEmpty()) {
- isDebug = qt5Core.front().contains(QStringLiteral("Qt5Cored.dll"), Qt::CaseInsensitive);
QStringList icuLibs = findDependentLibraries(qt5Core.front(), &errorMessage).filter(QStringLiteral("ICU"), Qt::CaseInsensitive);
if (!icuLibs.isEmpty()) {
// Find out the ICU version to add the data library icudtXX.dll, which does not show
@@ -247,7 +351,8 @@ int main(int argc, char **argv)
// Find the plugins and check whether ANGLE, D3D are required on the platform plugin.
QString platformPlugin;
const unsigned usedQtModules = qtModules(dependentQtLibs);
- const QStringList plugins = findQtPlugins(usedQtModules, isDebug, platform, &platformPlugin, &errorMessage);
+ const QStringList plugins = findQtPlugins(usedQtModules, qmakeVariables.value(QStringLiteral("QT_INSTALL_PLUGINS")),
+ isDebug, platform, &platformPlugin);
if (optVerboseLevel > 1)
std::fprintf(stderr, "Plugins: %s\n", qPrintable(plugins.join(QLatin1Char(','))));
@@ -312,9 +417,7 @@ int main(int argc, char **argv)
// Update Quick imports
if (optQuickImports && (usedQtModules & (Quick1Module | Quick2Module))) {
- const QStringList importNameFilters = QStringList() << QStringLiteral("*.qml")
- << QStringLiteral("*.js") << QStringLiteral("*.dll")
- << QStringLiteral("qmldir") << QStringLiteral("*.qmltypes");
+ const QmlDirectoryFileEntryFunction qmlFileEntryFunction(isDebug);
if (usedQtModules & Quick2Module) {
const QString quick2ImportPath = qmakeVariables.value(QStringLiteral("QT_INSTALL_QML"));
QStringList quick2Imports;
@@ -326,7 +429,7 @@ int main(int argc, char **argv)
if (usedQtModules & WebKitModule)
quick2Imports << QStringLiteral("QtWebKit");
foreach (const QString &quick2Import, quick2Imports) {
- if (!updateFile(quick2ImportPath + slash + quick2Import, importNameFilters, optDirectory, &errorMessage)) {
+ if (!updateFile(quick2ImportPath + slash + quick2Import, qmlFileEntryFunction, optDirectory, &errorMessage)) {
std::fprintf(stderr, "%s\n", qPrintable(errorMessage));
return 1;
}
@@ -338,7 +441,7 @@ int main(int argc, char **argv)
if (usedQtModules & WebKitModule)
quick1Imports << QStringLiteral("QtWebKit");
foreach (const QString &quick1Import, quick1Imports) {
- if (!updateFile(quick1ImportPath + slash + quick1Import, importNameFilters, optDirectory, &errorMessage)) {
+ if (!updateFile(quick1ImportPath + slash + quick1Import, qmlFileEntryFunction, optDirectory, &errorMessage)) {
std::fprintf(stderr, "%s\n", qPrintable(errorMessage));
return 1;
}
diff --git a/src/windeployqt/utils.cpp b/src/windeployqt/utils.cpp
index c8e8dcb0d..086d5b0b8 100644
--- a/src/windeployqt/utils.cpp
+++ b/src/windeployqt/utils.cpp
@@ -45,10 +45,7 @@
#include <QtCore/QFileInfo>
#include <QtCore/qt_windows.h>
#include <QtCore/QTemporaryFile>
-#include <QtCore/QFile>
-#include <QtCore/QDir>
#include <QtCore/QScopedPointer>
-#include <QtCore/QDateTime>
#include <QtCore/QStandardPaths>
#include <cstdio>
@@ -268,59 +265,6 @@ QString queryQMake(const QString &variable, QString *errorMessage)
return QString::fromLocal8Bit(stdOut).trimmed();
}
-static inline unsigned qtModuleForPlugin(const QString &subDirName)
-{
- if (subDirName == QLatin1String("accessible") || subDirName == QLatin1String("iconengines")
- || subDirName == QLatin1String("imageformats") || subDirName == QLatin1String("platforms")) {
- return GuiModule;
- }
- if (subDirName == QLatin1String("bearer"))
- return NetworkModule;
- if (subDirName == QLatin1String("sqldrivers"))
- return SqlModule;
- if (subDirName == QLatin1String("mediaservice") || subDirName == QLatin1String("playlistformats"))
- return MultimediaModule;
- if (subDirName == QLatin1String("printsupport"))
- return PrintSupportModule;
- if (subDirName == QLatin1String("qmltooling"))
- return Quick1Module | Quick2Module;
- return 0; // "designer"
-}
-
-QStringList findQtPlugins(unsigned usedQtModules, bool debug, Platform platform, QString *platformPlugin, QString *errorMessage)
-{
- const QString qtPluginsDirName = queryQMake(QStringLiteral("QT_INSTALL_PLUGINS"), errorMessage);
- if (qtPluginsDirName.isEmpty())
- return QStringList();
- QDir pluginsDir(qtPluginsDirName);
- QStringList result;
- foreach (const QString &subDirName, pluginsDir.entryList(QStringList(QLatin1String("*")), QDir::Dirs | QDir::NoDotAndDotDot)) {
- const unsigned module = qtModuleForPlugin(subDirName);
- if (module & usedQtModules) {
- const QString subDirPath = qtPluginsDirName + QLatin1Char('/') + subDirName;
- QDir subDir(subDirPath);
- // Filter for platform or any.
- QString filter;
- const bool isPlatformPlugin = subDirName == QLatin1String("platforms");
- if (isPlatformPlugin) {
- filter = platform == WinRt ? QStringLiteral("qwinrt") : QStringLiteral("qwindows");
- if (debug)
- filter.append(QLatin1Char('d'));
- } else {
- filter = QLatin1String(debug ? "*d" : "*[^d]");
- }
- filter += QStringLiteral(".dll");
- foreach (const QString &dll, subDir.entryList(QStringList(filter), QDir::Files)) {
- const QString plugin = subDirPath + QLatin1Char('/') + dll;
- result.push_back(plugin);
- if (isPlatformPlugin && platformPlugin)
- *platformPlugin = plugin;
- } // for filter
- } // type matches
- } // for plugin folder
- return result;
-}
-
// Update a file or directory.
bool updateFile(const QString &sourceFileName, const QStringList &nameFilters,
const QString &targetDirectory, QString *errorMessage)
diff --git a/src/windeployqt/utils.h b/src/windeployqt/utils.h
index 477a80e90..c58681cb1 100644
--- a/src/windeployqt/utils.h
+++ b/src/windeployqt/utils.h
@@ -44,6 +44,9 @@
#include <QStringList>
#include <QMap>
+#include <QtCore/QFile>
+#include <QtCore/QDir>
+#include <QtCore/QDateTime>
enum Platform { Windows, WinRt };
@@ -66,12 +69,8 @@ enum QtModule {
WebKitModule = 0x400
};
-QStringList findQtPlugins(unsigned usedQtModules, bool debug, Platform platform,
- QString *platformPlugin, QString *errorMessage);
bool updateFile(const QString &sourceFileName, const QStringList &nameFilters,
const QString &targetDirectory, QString *errorMessage);
-inline bool updateFile(const QString &sourceFileName, const QString &targetDirectory, QString *errorMessage)
-{ return updateFile(sourceFileName, QStringList(), targetDirectory, errorMessage); }
bool runProcess(const QString &commandLine, const QString &workingDirectory = QString(),
unsigned long *exitCode = 0, QByteArray *stdOut = 0, QByteArray *stdErr = 0,
QString *errorMessage = 0);
@@ -91,4 +90,98 @@ inline QStringList findDependentLibraries(const QString &peExecutableFileName, Q
extern int optVerboseLevel;
+// Recursively update a file or directory, matching DirectoryFileEntryFunction against the QDir
+// to obtain the files.
+template <class DirectoryFileEntryFunction>
+bool updateFile(const QString &sourceFileName,
+ DirectoryFileEntryFunction directoryFileEntryFunction,
+ const QString &targetDirectory,
+ QString *errorMessage)
+{
+ const QFileInfo sourceFileInfo(sourceFileName);
+ const QString targetFileName = targetDirectory + QLatin1Char('/') + sourceFileInfo.fileName();
+ if (optVerboseLevel > 1)
+ std::fprintf(stderr, "Checking %s, %s\n", qPrintable(sourceFileName), qPrintable(targetFileName));
+
+ if (!sourceFileInfo.exists()) {
+ *errorMessage = QString::fromLatin1("%1 does not exist.").arg(QDir::toNativeSeparators(sourceFileName));
+ return false;
+ }
+
+ if (sourceFileInfo.isSymLink()) {
+ *errorMessage = QString::fromLatin1("Symbolic links are not supported (%1).")
+ .arg(QDir::toNativeSeparators(sourceFileName));
+ return false;
+ }
+
+ const QFileInfo targetFileInfo(targetFileName);
+
+ if (sourceFileInfo.isDir()) {
+ if (targetFileInfo.exists()) {
+ if (!targetFileInfo.isDir()) {
+ *errorMessage = QString::fromLatin1("%1 already exists and is not a directory.")
+ .arg(QDir::toNativeSeparators(targetFileName));
+ return false;
+ } // Not a directory.
+ } else { // exists.
+ QDir d(targetDirectory);
+ std::printf("Creating %s.\n", qPrintable(targetFileName));
+ if (!d.mkdir(sourceFileInfo.fileName())) {
+ *errorMessage = QString::fromLatin1("Cannot create directory %1 under %2.")
+ .arg(sourceFileInfo.fileName(), QDir::toNativeSeparators(targetDirectory));
+ return false;
+ }
+ }
+ // Recurse into directory
+ QDir dir(sourceFileName);
+
+ const QStringList allEntries = directoryFileEntryFunction(dir) + dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+ foreach (const QString &entry, allEntries)
+ if (!updateFile(sourceFileName + QLatin1Char('/') + entry, directoryFileEntryFunction, targetFileName, errorMessage))
+ return false;
+ return true;
+ } // Source is directory.
+
+ if (targetFileInfo.exists()) {
+ if (targetFileInfo.lastModified() >= sourceFileInfo.lastModified()) {
+ if (optVerboseLevel)
+ std::printf("%s is up to date.\n", qPrintable(sourceFileInfo.fileName()));
+ return true;
+ }
+ QFile targetFile(targetFileName);
+ if (!targetFile.remove()) {
+ *errorMessage = QString::fromLatin1("Cannot remove existing file %1: %2")
+ .arg(QDir::toNativeSeparators(targetFileName), targetFile.errorString());
+ return false;
+ }
+ } // target exists
+ QFile file(sourceFileName);
+ if (optVerboseLevel)
+ std::printf("Updating %s.\n", qPrintable(sourceFileInfo.fileName()));
+ if (!file.copy(targetFileName)) {
+ *errorMessage = QString::fromLatin1("Cannot copy %1 to %2: %3")
+ .arg(QDir::toNativeSeparators(sourceFileName),
+ QDir::toNativeSeparators(targetFileName),
+ file.errorString());
+ return false;
+ }
+ return true;
+}
+
+// Base class to filter files by name filters functions to be passed to updateFile().
+class NameFilterFileEntryFunction {
+public:
+ explicit NameFilterFileEntryFunction(const QStringList &nameFilters) : m_nameFilters(nameFilters) {}
+ QStringList operator()(const QDir &dir) const { return dir.entryList(m_nameFilters, QDir::Files); }
+
+private:
+ const QStringList m_nameFilters;
+};
+
+// Convenience for all files.
+inline bool updateFile(const QString &sourceFileName, const QString &targetDirectory, QString *errorMessage)
+{
+ return updateFile(sourceFileName, NameFilterFileEntryFunction(QStringList()), targetDirectory, errorMessage);
+}
+
#endif // UTILS_H