From 42d32e468aa89631161884f07b3e25814c47b879 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Fri, 2 Aug 2019 13:18:46 +0200 Subject: [PATCH] Determine dependencies of Windows resource files Windows resource files support a subset of C preprocessor directives. Among others they can have #include directives. Use QMake's own scanner to retrieve the files that are included by a Windows resource file and add them to its dependencies. For the test case the TestCompiler class had to be extended: runCommand is now public, and commandOutput is less peculiar. Fixes: QTBUG-3859 Change-Id: I138703352c37c98297c0574a9a440510c1c494b8 Reviewed-by: Oliver Wolff Reviewed-by: Edward Welbourne --- qmake/generators/win32/mingw_make.cpp | 6 +++- qmake/generators/win32/winmakefile.cpp | 6 +++- tests/auto/tools/qmake/testcompiler.cpp | 14 ++++---- tests/auto/tools/qmake/testcompiler.h | 8 +++-- .../testdata/windows_resources/inter.inc | 1 + .../qmake/testdata/windows_resources/main.cpp | 1 + .../testdata/windows_resources/version.inc | 28 +++++++++++++++ .../windows_resources/windows_resources.pro | 4 +++ .../windows_resources/windows_resources.rc | 2 ++ tests/auto/tools/qmake/tst_qmake.cpp | 36 +++++++++++++++++-- 10 files changed, 93 insertions(+), 13 deletions(-) create mode 100644 tests/auto/tools/qmake/testdata/windows_resources/inter.inc create mode 100644 tests/auto/tools/qmake/testdata/windows_resources/main.cpp create mode 100644 tests/auto/tools/qmake/testdata/windows_resources/version.inc create mode 100644 tests/auto/tools/qmake/testdata/windows_resources/windows_resources.pro create mode 100644 tests/auto/tools/qmake/testdata/windows_resources/windows_resources.rc diff --git a/qmake/generators/win32/mingw_make.cpp b/qmake/generators/win32/mingw_make.cpp index 40114948c2..878291fae9 100644 --- a/qmake/generators/win32/mingw_make.cpp +++ b/qmake/generators/win32/mingw_make.cpp @@ -342,7 +342,11 @@ void MingwMakefileGenerator::writeRcFilePart(QTextStream &t) if (defines.isEmpty()) defines = ProString(" $(DEFINES)"); - t << escapeDependencyPath(var("RES_FILE")) << ": " << escapeDependencyPath(rc_file) << "\n\t" + addSourceFile(rc_file, QMakeSourceFileInfo::SEEK_DEPS); + const QStringList rcDeps = QStringList(rc_file) << dependencies(rc_file); + + t << escapeDependencyPath(var("RES_FILE")) << ": " + << escapeDependencyPaths(rcDeps).join(' ') << "\n\t" << var("QMAKE_RC") << " -i " << escapeFilePath(rc_file) << " -o " << fileVar("RES_FILE") << incPathStr << defines << "\n\n"; } diff --git a/qmake/generators/win32/winmakefile.cpp b/qmake/generators/win32/winmakefile.cpp index 392d825f5b..27d2a7c0a5 100644 --- a/qmake/generators/win32/winmakefile.cpp +++ b/qmake/generators/win32/winmakefile.cpp @@ -667,6 +667,9 @@ void Win32MakefileGenerator::writeRcFilePart(QTextStream &t) incPathStr += escapeFilePath(path); } + addSourceFile(rc_file, QMakeSourceFileInfo::SEEK_DEPS); + const QStringList rcDeps = QStringList(rc_file) << dependencies(rc_file); + // The resource tool may use defines. This might be the same defines passed in as the // compiler, since you may use these defines in the .rc file itself. // As the escape syntax for the command line defines for RC is different from that for CL, @@ -678,7 +681,8 @@ void Win32MakefileGenerator::writeRcFilePart(QTextStream &t) // Also, we need to add the _DEBUG define manually since the compiler defines this symbol // by itself, and we use it in the automatically created rc file when VERSION is defined // in the .pro file. - t << escapeDependencyPath(res_file) << ": " << escapeDependencyPath(rc_file) << "\n\t" + t << escapeDependencyPath(res_file) << ": " + << escapeDependencyPaths(rcDeps).join(' ') << "\n\t" << var("QMAKE_RC") << (project->isActiveConfig("debug") ? " -D_DEBUG" : "") << defines << incPathStr << " -fo " << escapeFilePath(res_file) << ' ' << escapeFilePath(rc_file); diff --git a/tests/auto/tools/qmake/testcompiler.cpp b/tests/auto/tools/qmake/testcompiler.cpp index ab68b5c725..b5aa4bec82 100644 --- a/tests/auto/tools/qmake/testcompiler.cpp +++ b/tests/auto/tools/qmake/testcompiler.cpp @@ -164,15 +164,17 @@ bool TestCompiler::runCommand(const QString &cmd, const QStringList &args, bool return errorOut(); } - child.setReadChannel(QProcess::StandardError); child.waitForFinished(-1); bool ok = child.exitStatus() == QProcess::NormalExit && (expectFail ^ (child.exitCode() == 0)); - foreach (const QByteArray &output, child.readAllStandardError().split('\n')) { - testOutput_.append(QString::fromLocal8Bit(output)); - - if (output.startsWith("Project MESSAGE: FAILED")) - ok = false; + for (auto channel : { QProcess::StandardOutput, QProcess::StandardError }) { + child.setReadChannel(channel); + while (!child.atEnd()) { + const QString output = QString::fromLocal8Bit(child.readLine()); + if (output.startsWith("Project MESSAGE: FAILED")) + ok = false; + testOutput_.append(output); + } } return ok ? true : errorOut(); diff --git a/tests/auto/tools/qmake/testcompiler.h b/tests/auto/tools/qmake/testcompiler.h index 50232669c0..73a79201fe 100644 --- a/tests/auto/tools/qmake/testcompiler.h +++ b/tests/auto/tools/qmake/testcompiler.h @@ -71,13 +71,17 @@ public: bool removeProject( const QString &workPath, const QString &project ); // removes the file specified by 'fileName' on the 'workPath' bool removeFile( const QString &workPath, const QString &fileName ); - // returns each line of stdout of the last command append with a "new line" character(s) to suit the platform + + // Returns each line of stdout/stderr of the last commands + // separated by platform-specific line endings. QString commandOutput() const; + // clear the results of storage of stdout for running previous commands void clearCommandOutput(); -private: bool runCommand(const QString &cmd, const QStringList &args, bool expectFail = false); + +private: bool errorOut(); QString makeCmd_; diff --git a/tests/auto/tools/qmake/testdata/windows_resources/inter.inc b/tests/auto/tools/qmake/testdata/windows_resources/inter.inc new file mode 100644 index 0000000000..0c2594214e --- /dev/null +++ b/tests/auto/tools/qmake/testdata/windows_resources/inter.inc @@ -0,0 +1 @@ +#include "version.inc" diff --git a/tests/auto/tools/qmake/testdata/windows_resources/main.cpp b/tests/auto/tools/qmake/testdata/windows_resources/main.cpp new file mode 100644 index 0000000000..237c8ce181 --- /dev/null +++ b/tests/auto/tools/qmake/testdata/windows_resources/main.cpp @@ -0,0 +1 @@ +int main() {} diff --git a/tests/auto/tools/qmake/testdata/windows_resources/version.inc b/tests/auto/tools/qmake/testdata/windows_resources/version.inc new file mode 100644 index 0000000000..b797c9acce --- /dev/null +++ b/tests/auto/tools/qmake/testdata/windows_resources/version.inc @@ -0,0 +1,28 @@ +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,0 + PRODUCTVERSION 1,0,0,0 + FILEFLAGS 0x0L + FILEFLAGSMASK 0x3fL + FILEOS 0x00040004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "CompanyName", "The Qt Company Ltd" + VALUE "FileDescription", "A Good File" + VALUE "FileVersion", "1.0.0.0" + VALUE "LegalCopyright", "Copyright (C) 2019 The Qt Company Ltd." + VALUE "InternalName", "foo" + VALUE "OriginalFilename", "foo.exe" + VALUE "ProductName", "A Good Product" + VALUE "ProductVersion", "1.0.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END diff --git a/tests/auto/tools/qmake/testdata/windows_resources/windows_resources.pro b/tests/auto/tools/qmake/testdata/windows_resources/windows_resources.pro new file mode 100644 index 0000000000..80a5e5c19a --- /dev/null +++ b/tests/auto/tools/qmake/testdata/windows_resources/windows_resources.pro @@ -0,0 +1,4 @@ +QT = core +SOURCES = main.cpp +RC_FILE = windows_resources.rc +TEMPLATE = app diff --git a/tests/auto/tools/qmake/testdata/windows_resources/windows_resources.rc b/tests/auto/tools/qmake/testdata/windows_resources/windows_resources.rc new file mode 100644 index 0000000000..4df652de35 --- /dev/null +++ b/tests/auto/tools/qmake/testdata/windows_resources/windows_resources.rc @@ -0,0 +1,2 @@ +#include "winver.h" +#include "inter.inc" diff --git a/tests/auto/tools/qmake/tst_qmake.cpp b/tests/auto/tools/qmake/tst_qmake.cpp index cda4c500e1..290ff49304 100644 --- a/tests/auto/tools/qmake/tst_qmake.cpp +++ b/tests/auto/tools/qmake/tst_qmake.cpp @@ -76,8 +76,10 @@ private slots: void findMocs(); void findDeps(); void rawString(); -#if defined(Q_OS_MAC) +#if defined(Q_OS_DARWIN) void bundle_spaces(); +#elif defined(Q_OS_WIN) + void windowsResources(); #endif void substitutes(); void project(); @@ -512,7 +514,8 @@ struct TempFile } }; -#if defined(Q_OS_MAC) +#if defined(Q_OS_DARWIN) + void tst_qmake::bundle_spaces() { QString workDir = base_path + "/testdata/bundle-spaces"; @@ -543,7 +546,34 @@ void tst_qmake::bundle_spaces() QVERIFY( !non_existing_file.exists() ); QVERIFY( test_compiler.removeMakefile(workDir) ); } -#endif // defined(Q_OS_MAC) + +#elif defined(Q_OS_WIN) // defined(Q_OS_DARWIN) + +void tst_qmake::windowsResources() +{ + QString workDir = base_path + "/testdata/windows_resources"; + QVERIFY(test_compiler.qmake(workDir, "windows_resources")); + QVERIFY(test_compiler.make(workDir)); + + // Another "make" must not rebuild the .res file + test_compiler.clearCommandOutput(); + QVERIFY(test_compiler.make(workDir)); + QVERIFY(!test_compiler.commandOutput().contains("windows_resources.rc")); + test_compiler.clearCommandOutput(); + + // Wait a second to make sure we get a new timestamp in the touch below + QTest::qWait(1000); + + // Touch the deepest include of the .rc file + QVERIFY(test_compiler.runCommand("cmd", QStringList{"/c", + "echo.>>" + QDir::toNativeSeparators(workDir + "/version.inc")})); + + // The next "make" must rebuild the .res file + QVERIFY(test_compiler.make(workDir)); + QVERIFY(test_compiler.commandOutput().contains("windows_resources.rc")); +} + +#endif // defined(Q_OS_WIN) void tst_qmake::substitutes() {