From d20773824529d191e7b483b505107dce6c1b1c3d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 18 Nov 2016 16:54:09 +0100 Subject: Fix missing last modification time stamp in qrc content The time stamp is added at the end of the node information and consequently this also bumps the version. Task-number: QTBUG-57182 Change-Id: Ia10e006f28c0b168b2bcd74ed8b7098f84d10af3 Reviewed-by: hjk --- src/corelib/io/qresource.cpp | 65 +++++++++++++++++----- src/corelib/io/qresource.h | 1 + src/tools/rcc/rcc.cpp | 46 +++++++++++++-- src/tools/rcc/rcc.h | 1 + .../io/qresourceengine/tst_qresourceengine.cpp | 15 +++++ tests/auto/tools/rcc/data/images/images.expected | 10 +++- tests/auto/tools/rcc/tst_rcc.cpp | 17 +++++- 7 files changed, 133 insertions(+), 22 deletions(-) diff --git a/src/corelib/io/qresource.cpp b/src/corelib/io/qresource.cpp index 7fe3753da4..72042e1600 100644 --- a/src/corelib/io/qresource.cpp +++ b/src/corelib/io/qresource.cpp @@ -101,35 +101,38 @@ class QResourceRoot Directory = 0x02 }; const uchar *tree, *names, *payloads; - inline int findOffset(int node) const { return node * 14; } //sizeof each tree element + int version; + inline int findOffset(int node) const { return node * (14 + (version >= 0x02 ? 8 : 0)); } //sizeof each tree element uint hash(int node) const; QString name(int node) const; short flags(int node) const; public: mutable QAtomicInt ref; - inline QResourceRoot(): tree(0), names(0), payloads(0) {} - inline QResourceRoot(const uchar *t, const uchar *n, const uchar *d) { setSource(t, n, d); } + inline QResourceRoot(): tree(0), names(0), payloads(0), version(0) {} + inline QResourceRoot(int version, const uchar *t, const uchar *n, const uchar *d) { setSource(version, t, n, d); } virtual ~QResourceRoot() { } int findNode(const QString &path, const QLocale &locale=QLocale()) const; inline bool isContainer(int node) const { return flags(node) & Directory; } inline bool isCompressed(int node) const { return flags(node) & Compressed; } const uchar *data(int node, qint64 *size) const; + QDateTime lastModified(int node) const; QStringList children(int node) const; virtual QString mappingRoot() const { return QString(); } bool mappingRootSubdir(const QString &path, QString *match=0) const; inline bool operator==(const QResourceRoot &other) const - { return tree == other.tree && names == other.names && payloads == other.payloads; } + { return tree == other.tree && names == other.names && payloads == other.payloads && version == other.version; } inline bool operator!=(const QResourceRoot &other) const { return !operator==(other); } enum ResourceRootType { Resource_Builtin, Resource_File, Resource_Buffer }; virtual ResourceRootType type() const { return Resource_Builtin; } protected: - inline void setSource(const uchar *t, const uchar *n, const uchar *d) { + inline void setSource(int v, const uchar *t, const uchar *n, const uchar *d) { tree = t; names = n; payloads = d; + version = v; } }; @@ -231,6 +234,7 @@ public: mutable qint64 size; mutable const uchar *data; mutable QStringList children; + mutable QDateTime lastModified; QResource *q_ptr; Q_DECLARE_PUBLIC(QResource) @@ -244,6 +248,7 @@ QResourcePrivate::clear() data = 0; size = 0; children.clear(); + lastModified = QDateTime(); container = 0; for(int i = 0; i < related.size(); ++i) { QResourceRoot *root = related.at(i); @@ -274,6 +279,7 @@ QResourcePrivate::load(const QString &file) size = 0; compressed = 0; } + lastModified = res->lastModified(node); } else if(res->isContainer(node) != container) { qWarning("QResourceInfo: Resource [%s] has both data and children!", file.toLatin1().constData()); } @@ -284,6 +290,7 @@ QResourcePrivate::load(const QString &file) data = 0; size = 0; compressed = 0; + lastModified = QDateTime(); res->ref.ref(); related.append(res); } @@ -513,6 +520,17 @@ const uchar *QResource::data() const return d->data; } +/*! + Returns the date and time when the file was last modified before + packaging into a resource. +*/ +QDateTime QResource::lastModified() const +{ + Q_D(const QResource); + d->ensureInitialized(); + return d->lastModified; +} + /*! Returns \c true if the resource represents a directory and thus may have children() in it, false if it represents a file. @@ -780,6 +798,24 @@ const uchar *QResourceRoot::data(int node, qint64 *size) const *size = 0; return 0; } + +QDateTime QResourceRoot::lastModified(int node) const +{ + if (node == -1 || version < 0x02) + return QDateTime(); + + const int offset = findOffset(node) + 14; + + const quint64 timeStamp = (quint64(tree[offset+0]) << 56) + (quint64(tree[offset+1]) << 48) + + (quint64(tree[offset+2]) << 40) + (quint64(tree[offset+3]) << 32) + + (quint64(tree[offset+4]) << 24) + (quint64(tree[offset+5]) << 16) + + (quint64(tree[offset+6]) << 8) + (quint64(tree[offset+7])); + if (timeStamp == 0) + return QDateTime(); + + return QDateTime::fromMSecsSinceEpoch(timeStamp); +} + QStringList QResourceRoot::children(int node) const { if(node == -1) @@ -829,9 +865,9 @@ Q_CORE_EXPORT bool qRegisterResourceData(int version, const unsigned char *tree, const unsigned char *name, const unsigned char *data) { QMutexLocker lock(resourceMutex()); - if(version == 0x01 && resourceList()) { + if ((version == 0x01 || version == 0x2) && resourceList()) { bool found = false; - QResourceRoot res(tree, name, data); + QResourceRoot res(version, tree, name, data); for(int i = 0; i < resourceList()->size(); ++i) { if(*resourceList()->at(i) == res) { found = true; @@ -839,7 +875,7 @@ Q_CORE_EXPORT bool qRegisterResourceData(int version, const unsigned char *tree, } } if(!found) { - QResourceRoot *root = new QResourceRoot(tree, name, data); + QResourceRoot *root = new QResourceRoot(version, tree, name, data); root->ref.ref(); resourceList()->append(root); } @@ -852,8 +888,8 @@ Q_CORE_EXPORT bool qUnregisterResourceData(int version, const unsigned char *tre const unsigned char *name, const unsigned char *data) { QMutexLocker lock(resourceMutex()); - if(version == 0x01 && resourceList()) { - QResourceRoot res(tree, name, data); + if ((version == 0x01 || version == 0x02) && resourceList()) { + QResourceRoot res(version, tree, name, data); for(int i = 0; i < resourceList()->size(); ) { if(*resourceList()->at(i) == res) { QResourceRoot *root = resourceList()->takeAt(i); @@ -919,9 +955,9 @@ public: if (size >= 0 && (tree_offset >= size || data_offset >= size || name_offset >= size)) return false; - if(version == 0x01) { + if (version == 0x01 || version == 0x02) { buffer = b; - setSource(b+tree_offset, b+name_offset, b+data_offset); + setSource(version, b+tree_offset, b+name_offset, b+data_offset); return true; } return false; @@ -1430,8 +1466,11 @@ QString QResourceFileEngine::owner(FileOwner) const return QString(); } -QDateTime QResourceFileEngine::fileTime(FileTime) const +QDateTime QResourceFileEngine::fileTime(FileTime time) const { + Q_D(const QResourceFileEngine); + if (time == ModificationTime) + return d->resource.lastModified(); return QDateTime(); } diff --git a/src/corelib/io/qresource.h b/src/corelib/io/qresource.h index a50bbbdaca..895cf0456e 100644 --- a/src/corelib/io/qresource.h +++ b/src/corelib/io/qresource.h @@ -69,6 +69,7 @@ public: bool isCompressed() const; qint64 size() const; const uchar *data() const; + QDateTime lastModified() const; static void addSearchPath(const QString &path); static QStringList searchPaths(); diff --git a/src/tools/rcc/rcc.cpp b/src/tools/rcc/rcc.cpp index 18772d2816..c8ac554cd4 100644 --- a/src/tools/rcc/rcc.cpp +++ b/src/tools/rcc/rcc.cpp @@ -203,6 +203,12 @@ void RCCFileInfo::writeDataInfo(RCCResourceLibrary &lib) } if (text || pass1) lib.writeChar('\n'); + + // last modified time stamp + const QDateTime lastModified = m_fileInfo.lastModified(); + lib.writeNumber8(quint64(lastModified.isValid() ? lastModified.toMSecsSinceEpoch() : 0)); + if (text || pass1) + lib.writeChar('\n'); } qint64 RCCFileInfo::writeDataBlob(RCCResourceLibrary &lib, qint64 offset, @@ -833,6 +839,38 @@ void RCCResourceLibrary::writeNumber4(quint32 number) } } +void RCCResourceLibrary::writeNumber8(quint64 number) +{ + if (m_format == RCCResourceLibrary::Pass2) { + m_outDevice->putChar(char(number >> 56)); + m_outDevice->putChar(char(number >> 48)); + m_outDevice->putChar(char(number >> 40)); + m_outDevice->putChar(char(number >> 32)); + m_outDevice->putChar(char(number >> 24)); + m_outDevice->putChar(char(number >> 16)); + m_outDevice->putChar(char(number >> 8)); + m_outDevice->putChar(char(number)); + } else if (m_format == RCCResourceLibrary::Binary) { + writeChar(number >> 56); + writeChar(number >> 48); + writeChar(number >> 40); + writeChar(number >> 32); + writeChar(number >> 24); + writeChar(number >> 16); + writeChar(number >> 8); + writeChar(number); + } else { + writeHex(number >> 56); + writeHex(number >> 48); + writeHex(number >> 40); + writeHex(number >> 32); + writeHex(number >> 24); + writeHex(number >> 16); + writeHex(number >> 8); + writeHex(number); + } +} + bool RCCResourceLibrary::writeHeader() { if (m_format == C_Code || m_format == Pass1) { @@ -1076,7 +1114,7 @@ bool RCCResourceLibrary::writeInitializer() if (m_root) { writeString(" "); writeAddNamespaceFunction("qRegisterResourceData"); - writeString("\n (0x01, qt_resource_struct, " + writeString("\n (0x02, qt_resource_struct, " "qt_resource_name, qt_resource_data);\n"); } writeString(" return 1;\n"); @@ -1097,7 +1135,7 @@ bool RCCResourceLibrary::writeInitializer() if (m_root) { writeString(" "); writeAddNamespaceFunction("qUnregisterResourceData"); - writeString("\n (0x01, qt_resource_struct, " + writeString("\n (0x02, qt_resource_struct, " "qt_resource_name, qt_resource_data);\n"); } writeString(" return 1;\n"); @@ -1114,10 +1152,10 @@ bool RCCResourceLibrary::writeInitializer() } else if (m_format == Binary) { int i = 4; char *p = m_out.data(); - p[i++] = 0; // 0x01 + p[i++] = 0; // 0x02 p[i++] = 0; p[i++] = 0; - p[i++] = 1; + p[i++] = 2; p[i++] = (m_treeOffset >> 24) & 0xff; p[i++] = (m_treeOffset >> 16) & 0xff; diff --git a/src/tools/rcc/rcc.h b/src/tools/rcc/rcc.h index 8d0d83e00c..157cd4809f 100644 --- a/src/tools/rcc/rcc.h +++ b/src/tools/rcc/rcc.h @@ -118,6 +118,7 @@ private: void writeHex(quint8 number); void writeNumber2(quint16 number); void writeNumber4(quint32 number); + void writeNumber8(quint64 number); void writeChar(char c) { m_out.append(c); } void writeByteArray(const QByteArray &); void write(const char *, int len); diff --git a/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp b/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp index 561ab193c6..7fdd00876f 100644 --- a/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp +++ b/tests/auto/corelib/io/qresourceengine/tst_qresourceengine.cpp @@ -55,6 +55,7 @@ private slots: void searchPath(); void doubleSlashInRoot(); void setLocale(); + void lastModified(); private: const QString m_runtimeResourceRcc; @@ -489,6 +490,20 @@ void tst_QResourceEngine::setLocale() QLocale::setDefault(QLocale::system()); } +void tst_QResourceEngine::lastModified() +{ + { + QFileInfo fi(":/"); + QVERIFY(fi.exists()); + QVERIFY2(!fi.lastModified().isValid(), qPrintable(fi.lastModified().toString())); + } + { + QFileInfo fi(":/search_file.txt"); + QVERIFY(fi.exists()); + QVERIFY(fi.lastModified().isValid()); + } +} + QTEST_MAIN(tst_QResourceEngine) #include "tst_qresourceengine.moc" diff --git a/tests/auto/tools/rcc/data/images/images.expected b/tests/auto/tools/rcc/data/images/images.expected index 1f0157d51c..eb5d9222c8 100644 --- a/tests/auto/tools/rcc/data/images/images.expected +++ b/tests/auto/tools/rcc/data/images/images.expected @@ -79,16 +79,22 @@ static const unsigned char qt_resource_name[] = { static const unsigned char qt_resource_struct[] = { // : 0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1, +0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, // :/images 0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x3,0x0,0x0,0x0,0x2, +0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, // :/images/subdir 0x0,0x0,0x0,0x12,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x5, +0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, // :/images/square.png 0x0,0x0,0x0,0x3e,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0xa9, +TIMESTAMP:images/square.png // :/images/circle.png 0x0,0x0,0x0,0x24,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0, +TIMESTAMP:images/circle.png // :/images/subdir/triangle.png 0x0,0x0,0x0,0x58,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x1,0xb, +TIMESTAMP:images/subdir/triangle.png }; @@ -120,7 +126,7 @@ int QT_RCC_MANGLE_NAMESPACE(qInitResources)(); int QT_RCC_MANGLE_NAMESPACE(qInitResources)() { QT_RCC_PREPEND_NAMESPACE(qRegisterResourceData) - (0x01, qt_resource_struct, qt_resource_name, qt_resource_data); + (0x02, qt_resource_struct, qt_resource_name, qt_resource_data); return 1; } @@ -128,7 +134,7 @@ int QT_RCC_MANGLE_NAMESPACE(qCleanupResources)(); int QT_RCC_MANGLE_NAMESPACE(qCleanupResources)() { QT_RCC_PREPEND_NAMESPACE(qUnregisterResourceData) - (0x01, qt_resource_struct, qt_resource_name, qt_resource_data); + (0x02, qt_resource_struct, qt_resource_name, qt_resource_data); return 1; } diff --git a/tests/auto/tools/rcc/tst_rcc.cpp b/tests/auto/tools/rcc/tst_rcc.cpp index 8d95d06e30..54a2854ede 100644 --- a/tests/auto/tools/rcc/tst_rcc.cpp +++ b/tests/auto/tools/rcc/tst_rcc.cpp @@ -92,12 +92,23 @@ static QString doCompare(const QStringList &actual, const QStringList &expected) QByteArray ba; for (int i = 0, n = expected.size(); i != n; ++i) { - if (expected.at(i).startsWith("IGNORE:")) + QString expectedLine = expected.at(i); + if (expectedLine.startsWith("IGNORE:")) continue; - if (expected.at(i) != actual.at(i)) { + if (expectedLine.startsWith("TIMESTAMP:")) { + const QString relativePath = expectedLine.mid(strlen("TIMESTAMP:")); + const quint64 timeStamp = QFileInfo(relativePath).lastModified().toMSecsSinceEpoch(); + expectedLine.clear(); + for (int shift = 56; shift >= 0; shift -= 8) { + expectedLine.append(QLatin1String("0x")); + expectedLine.append(QString::number(quint8(timeStamp >> shift), 16)); + expectedLine.append(QLatin1Char(',')); + } + } + if (expectedLine != actual.at(i)) { qDebug() << "LINES" << i << "DIFFER"; ba.append( - "\n<<<<<< actual\n" + actual.at(i) + "\n======\n" + expected.at(i) + "\n<<<<<< actual\n" + actual.at(i) + "\n======\n" + expectedLine + "\n>>>>>> expected\n" ); } -- cgit v1.2.3