diff options
author | hjk <hjk@qt.io> | 2019-03-01 12:55:13 +0100 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2019-03-07 10:45:42 +0000 |
commit | f91aae6397378f7f86a3dd320f4e2caa3843a5de (patch) | |
tree | eb60056b01e235f748874bfa43a98c82c1508403 | |
parent | de1e15af4460c164913cba00686b1e78bb6c2c9e (diff) |
rcc: Support Python as output format
Start with rcc -g python|python2 $name.qrc.
[ChangeLog][rcc] Added support for Python as output format.
Done-with: Friedemann Kleint <Friedemann.Kleint@qt.io>
Fixes: PYSIDE-855
Change-Id: I97a642c3721d6d95b7cd0972d21abb0b2752fd4f
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
-rw-r--r-- | src/tools/rcc/main.cpp | 19 | ||||
-rw-r--r-- | src/tools/rcc/rcc.cpp | 201 | ||||
-rw-r--r-- | src/tools/rcc/rcc.h | 3 | ||||
-rw-r--r-- | tests/auto/tools/rcc/data/sizes/size-2-0-35-1_python.expected | 68 | ||||
-rw-r--r-- | tests/auto/tools/rcc/tst_rcc.cpp | 41 |
5 files changed, 306 insertions, 26 deletions
diff --git a/src/tools/rcc/main.cpp b/src/tools/rcc/main.cpp index 6e8c13be15..0eb6766b5a 100644 --- a/src/tools/rcc/main.cpp +++ b/src/tools/rcc/main.cpp @@ -155,6 +155,11 @@ int runRcc(int argc, char *argv[]) QCommandLineOption binaryOption(QStringLiteral("binary"), QStringLiteral("Output a binary file for use as a dynamic resource.")); parser.addOption(binaryOption); + QCommandLineOption generatorOption(QStringList{QStringLiteral("g"), QStringLiteral("generator")}); + generatorOption.setDescription(QStringLiteral("Select generator.")); + generatorOption.setValueName(QStringLiteral("cpp|python|python2")); + parser.addOption(generatorOption); + QCommandLineOption passOption(QStringLiteral("pass"), QStringLiteral("Pass number for big resources"), QStringLiteral("number")); parser.addOption(passOption); @@ -220,6 +225,18 @@ int runRcc(int argc, char *argv[]) library.setCompressThreshold(parser.value(thresholdOption).toInt()); if (parser.isSet(binaryOption)) library.setFormat(RCCResourceLibrary::Binary); + if (parser.isSet(generatorOption)) { + auto value = parser.value(generatorOption); + if (value == QLatin1String("cpp")) + library.setFormat(RCCResourceLibrary::C_Code); + else if (value == QLatin1String("python")) + library.setFormat(RCCResourceLibrary::Python3_Code); + else if (value == QLatin1String("python2")) + library.setFormat(RCCResourceLibrary::Python2_Code); + else + errorMsg = QLatin1String("Invalid generator: ") + value; + } + if (parser.isSet(passOption)) { if (parser.value(passOption) == QLatin1String("1")) library.setFormat(RCCResourceLibrary::Pass1); @@ -280,6 +297,8 @@ int runRcc(int argc, char *argv[]) switch (library.format()) { case RCCResourceLibrary::C_Code: case RCCResourceLibrary::Pass1: + case RCCResourceLibrary::Python3_Code: + case RCCResourceLibrary::Python2_Code: mode = QIODevice::WriteOnly | QIODevice::Text; break; case RCCResourceLibrary::Pass2: diff --git a/src/tools/rcc/rcc.cpp b/src/tools/rcc/rcc.cpp index 862e574f2d..704c336860 100644 --- a/src/tools/rcc/rcc.cpp +++ b/src/tools/rcc/rcc.cpp @@ -68,6 +68,17 @@ enum { #define writeString(s) write(s, sizeof(s)) +static const char pythonHeader1[] = +R"(# Created by: object code +# Created by: The Resource Compiler for Qt version )"; + +static const char pythonHeader2[] = R"( +# WARNING! All changes made in this file will be lost! + +from PySide2 import QtCore + +)"; + void RCCResourceLibrary::write(const char *str, int len) { --len; // trailing \0 on string literals... @@ -176,6 +187,8 @@ void RCCFileInfo::writeDataInfo(RCCResourceLibrary &lib) { const bool text = lib.m_format == RCCResourceLibrary::C_Code; const bool pass1 = lib.m_format == RCCResourceLibrary::Pass1; + const bool python = lib.m_format == RCCResourceLibrary::Python3_Code + || lib.m_format == RCCResourceLibrary::Python2_Code; //some info if (text || pass1) { if (m_language != QLocale::C) { @@ -222,6 +235,8 @@ void RCCFileInfo::writeDataInfo(RCCResourceLibrary &lib) } if (text || pass1) lib.writeChar('\n'); + else if (python) + lib.writeString("\\\n"); if (lib.formatVersion() >= 2) { // last modified time stamp @@ -236,6 +251,8 @@ void RCCFileInfo::writeDataInfo(RCCResourceLibrary &lib) lib.writeNumber8(lastmod); if (text || pass1) lib.writeChar('\n'); + else if (python) + lib.writeString("\\\n"); } } @@ -246,6 +263,8 @@ qint64 RCCFileInfo::writeDataBlob(RCCResourceLibrary &lib, qint64 offset, const bool pass1 = lib.m_format == RCCResourceLibrary::Pass1; const bool pass2 = lib.m_format == RCCResourceLibrary::Pass2; const bool binary = lib.m_format == RCCResourceLibrary::Binary; + const bool python = lib.m_format == RCCResourceLibrary::Python3_Code + || lib.m_format == RCCResourceLibrary::Python2_Code; //capture the offset m_dataOffset = offset; @@ -343,20 +362,24 @@ qint64 RCCFileInfo::writeDataBlob(RCCResourceLibrary &lib, qint64 offset, } // write the length - - if (text || binary || pass2) + if (text || binary || pass2 || python) lib.writeNumber4(data.size()); if (text || pass1) lib.writeString("\n "); + else if (python) + lib.writeString("\\\n"); offset += 4; // write the payload const char *p = data.constData(); - if (text) { + if (text || python) { for (int i = data.size(), j = 0; --i >= 0; --j) { lib.writeHex(*p++); if (j == 0) { - lib.writeString("\n "); + if (text) + lib.writeString("\n "); + else + lib.writeString("\\\n"); j = 16; } } @@ -368,6 +391,9 @@ qint64 RCCFileInfo::writeDataBlob(RCCResourceLibrary &lib, qint64 offset, // done if (text || pass1) lib.writeString("\n "); + else if (python) + lib.writeString("\\\n"); + return offset; } @@ -375,6 +401,8 @@ qint64 RCCFileInfo::writeDataName(RCCResourceLibrary &lib, qint64 offset) { const bool text = lib.m_format == RCCResourceLibrary::C_Code; const bool pass1 = lib.m_format == RCCResourceLibrary::Pass1; + const bool python = lib.m_format == RCCResourceLibrary::Python3_Code + || lib.m_format == RCCResourceLibrary::Python2_Code; // capture the offset m_nameOffset = offset; @@ -390,12 +418,16 @@ qint64 RCCFileInfo::writeDataName(RCCResourceLibrary &lib, qint64 offset) lib.writeNumber2(m_name.length()); if (text || pass1) lib.writeString("\n "); + else if (python) + lib.writeString("\\\n"); offset += 2; // write the hash lib.writeNumber4(qt_hash(m_name)); if (text || pass1) lib.writeString("\n "); + else if (python) + lib.writeString("\\\n"); offset += 4; // write the m_name @@ -404,12 +436,17 @@ qint64 RCCFileInfo::writeDataName(RCCResourceLibrary &lib, qint64 offset) lib.writeNumber2(unicode[i].unicode()); if ((text || pass1) && i % 16 == 0) lib.writeString("\n "); + else if (python && i % 16 == 0) + lib.writeString("\\\n"); } offset += m_name.length()*2; // done if (text || pass1) lib.writeString("\n "); + else if (python) + lib.writeString("\\\n"); + return offset; } @@ -959,18 +996,37 @@ void RCCResourceLibrary::writeDecimal(int value) write(buf, n + 1); // write() takes a size including terminating NUL } +static const char hexDigits[] = "0123456789abcdef"; + +inline void RCCResourceLibrary::write2HexDigits(quint8 number) +{ + writeChar(hexDigits[number >> 4]); + writeChar(hexDigits[number & 0xf]); +} + void RCCResourceLibrary::writeHex(quint8 tmp) { - const char digits[] = "0123456789abcdef"; - writeChar('0'); - writeChar('x'); - if (tmp < 16) { - writeChar(digits[tmp]); - } else { - writeChar(digits[tmp >> 4]); - writeChar(digits[tmp & 0xf]); + switch (m_format) { + case RCCResourceLibrary::Python3_Code: + case RCCResourceLibrary::Python2_Code: + if (tmp >= 32 && tmp < 127 && tmp != '"' && tmp != '\\') { + writeChar(char(tmp)); + } else { + writeChar('\\'); + writeChar('x'); + write2HexDigits(tmp); + } + break; + default: + writeChar('0'); + writeChar('x'); + if (tmp < 16) + writeChar(hexDigits[tmp]); + else + write2HexDigits(tmp); + writeChar(','); + break; } - writeChar(','); } void RCCResourceLibrary::writeNumber2(quint16 number) @@ -1038,7 +1094,9 @@ void RCCResourceLibrary::writeNumber8(quint64 number) bool RCCResourceLibrary::writeHeader() { - if (m_format == C_Code || m_format == Pass1) { + switch (m_format) { + case C_Code: + case Pass1: writeString("/****************************************************************************\n"); writeString("** Resource object code\n"); writeString("**\n"); @@ -1047,7 +1105,17 @@ bool RCCResourceLibrary::writeHeader() writeString("\n**\n"); writeString("** WARNING! All changes made in this file will be lost!\n"); writeString( "*****************************************************************************/\n\n"); - } else if (m_format == Binary) { + break; + case Python3_Code: + case Python2_Code: + writeString("# Resource object code (Python "); + writeChar(m_format == Python3_Code ? '3' : '2'); + writeString(")\n"); + writeString(pythonHeader1); + writeByteArray(QT_VERSION_STR); + writeString(pythonHeader2); + break; + case Binary: writeString("qres"); writeNumber4(0); writeNumber4(0); @@ -1055,6 +1123,9 @@ bool RCCResourceLibrary::writeHeader() writeNumber4(0); if (m_formatVersion >= 3) writeNumber4(m_overallFlags); + break; + default: + break; } return true; } @@ -1062,10 +1133,21 @@ bool RCCResourceLibrary::writeHeader() bool RCCResourceLibrary::writeDataBlobs() { Q_ASSERT(m_errorDevice); - if (m_format == C_Code) { + switch (m_format) { + case C_Code: writeString("static const unsigned char qt_resource_data[] = {\n"); - } else if (m_format == Binary) { + break; + case Python3_Code: + writeString("qt_resource_data = b\"\\\n"); + break; + case Python2_Code: + writeString("qt_resource_data = \"\\\n"); + break; + case Binary: m_dataOffset = m_out.size(); + break; + default: + break; } if (!m_root) @@ -1091,24 +1173,46 @@ bool RCCResourceLibrary::writeDataBlobs() } } } - if (m_format == C_Code) + switch (m_format) { + case C_Code: writeString("\n};\n\n"); - else if (m_format == Pass1) { + break; + case Python3_Code: + case Python2_Code: + writeString("\"\n\n"); + break; + case Pass1: if (offset < 8) offset = 8; writeString("\nstatic const unsigned char qt_resource_data["); writeByteArray(QByteArray::number(offset)); writeString("] = { 'Q', 'R', 'C', '_', 'D', 'A', 'T', 'A' };\n\n"); + break; + default: + break; } return true; } bool RCCResourceLibrary::writeDataNames() { - if (m_format == C_Code || m_format == Pass1) + switch (m_format) { + case C_Code: + case Pass1: writeString("static const unsigned char qt_resource_name[] = {\n"); - else if (m_format == Binary) + break; + case Python3_Code: + writeString("qt_resource_name = b\"\\\n"); + break; + case Python2_Code: + writeString("qt_resource_name = \"\\\n"); + break; + case Binary: m_namesOffset = m_out.size(); + break; + default: + break; + } QHash<QString, int> names; QStack<RCCFileInfo*> pending; @@ -1133,8 +1237,18 @@ bool RCCResourceLibrary::writeDataNames() } } } - if (m_format == C_Code || m_format == Pass1) + switch (m_format) { + case C_Code: + case Pass1: writeString("\n};\n\n"); + break; + case Python3_Code: + case Python2_Code: + writeString("\"\n\n"); + break; + default: + break; + } return true; } @@ -1149,10 +1263,24 @@ struct qt_rcc_compare_hash bool RCCResourceLibrary::writeDataStructure() { - if (m_format == C_Code || m_format == Pass1) + switch (m_format) { + case C_Code: + case Pass1: writeString("static const unsigned char qt_resource_struct[] = {\n"); - else if (m_format == Binary) + break; + case Python3_Code: + writeString("qt_resource_struct = b\"\\\n"); + break; + case Python2_Code: + writeString("qt_resource_struct = \"\\\n"); + break; + case Binary: m_treeOffset = m_out.size(); + break; + default: + break; + } + QStack<RCCFileInfo*> pending; if (!m_root) @@ -1196,8 +1324,18 @@ bool RCCResourceLibrary::writeDataStructure() pending.push(child); } } - if (m_format == C_Code || m_format == Pass1) + switch (m_format) { + case C_Code: + case Pass1: writeString("\n};\n\n"); + break; + case Python3_Code: + case Python2_Code: + writeString("\"\n\n"); + break; + default: + break; + } return true; } @@ -1387,6 +1525,19 @@ bool RCCResourceLibrary::writeInitializer() p[i++] = (m_overallFlags >> 8) & 0xff; p[i++] = (m_overallFlags >> 0) & 0xff; } + } else if (m_format == Python3_Code || m_format == Python2_Code) { + writeString(R"(def qInitResources(): + QtCore.qRegisterResourceData(0x)"); + write2HexDigits(m_formatVersion); + writeString(R"(, qt_resource_struct, qt_resource_name, qt_resource_data) + +def qCleanupResources(): + QtCore.qUnregisterResourceData(0x)"); + write2HexDigits(m_formatVersion); + writeString(R"(, qt_resource_struct, qt_resource_name, qt_resource_data) + +qInitResources() +)"); } return true; } diff --git a/src/tools/rcc/rcc.h b/src/tools/rcc/rcc.h index ad1c5cd166..b301355e4f 100644 --- a/src/tools/rcc/rcc.h +++ b/src/tools/rcc/rcc.h @@ -58,7 +58,7 @@ public: bool readFiles(bool listMode, QIODevice &errorDevice); - enum Format { Binary, C_Code, Pass1, Pass2 }; + enum Format { Binary, C_Code, Pass1, Pass2, Python3_Code, Python2_Code }; void setFormat(Format f) { m_format = f; } Format format() const { return m_format; } @@ -136,6 +136,7 @@ private: void writeAddNamespaceFunction(const QByteArray &name); void writeDecimal(int value); void writeHex(quint8 number); + void write2HexDigits(quint8 number); void writeNumber2(quint16 number); void writeNumber4(quint32 number); void writeNumber8(quint64 number); diff --git a/tests/auto/tools/rcc/data/sizes/size-2-0-35-1_python.expected b/tests/auto/tools/rcc/data/sizes/size-2-0-35-1_python.expected new file mode 100644 index 0000000000..9dcd131af5 --- /dev/null +++ b/tests/auto/tools/rcc/data/sizes/size-2-0-35-1_python.expected @@ -0,0 +1,68 @@ +# Resource object code (Python 3) +# Created by: object code +# Created by: The Resource Compiler for Qt version 5.14.0 +# WARNING! All changes made in this file will be lost! + +from PySide2 import QtCore + +qt_resource_data = b"\ +\x00\x00\x00\x02\ +0\ +1\ +\x00\x00\x00#\ +0\ +123456789 012345\ +6789 0123456789 \ +12\ +\x00\x00\x00\x01\ +@\ +\ +\x00\x00\x00\x00\ +\ +" + +qt_resource_name = b"\ +\x00\x04\ +\x00\x06\xa8\xa1\ +\x00d\ +\x00a\x00t\x00a\ +\x00\x0a\ +\x04\x08\x0a\xb4\ +\x00d\ +\x00a\x00t\x00a\x00-\x002\x00.\x00t\x00x\x00t\ +\x00\x0b\ +\x00\xb5Ot\ +\x00d\ +\x00a\x00t\x00a\x00-\x003\x005\x00.\x00t\x00x\x00t\ +\x00\x0a\ +\x04\x11\x0a\xb4\ +\x00d\ +\x00a\x00t\x00a\x00-\x001\x00.\x00t\x00x\x00t\ +\x00\x0a\ +\x04\x0e\x0a\xb4\ +\x00d\ +\x00a\x00t\x00a\x00-\x000\x00.\x00t\x00x\x00t\ +" + +qt_resource_struct = b"\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x02\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00(\x00\x00\x00\x00\x00\x01\x00\x00\x00\x06\ +IGNORE: (time stamp) +\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +IGNORE: (time stamp) +\x00\x00\x00^\x00\x00\x00\x00\x00\x01\x00\x00\x002\ +IGNORE: (time stamp) +\x00\x00\x00D\x00\x00\x00\x00\x00\x01\x00\x00\x00-\ +IGNORE: (time stamp) +" + +def qInitResources(): + QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data) + +def qCleanupResources(): + QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data) + +qInitResources() diff --git a/tests/auto/tools/rcc/tst_rcc.cpp b/tests/auto/tools/rcc/tst_rcc.cpp index 2c63b0c770..24fd79cf19 100644 --- a/tests/auto/tools/rcc/tst_rcc.cpp +++ b/tests/auto/tools/rcc/tst_rcc.cpp @@ -89,6 +89,8 @@ private slots: void readback_data(); void readback(); + void python(); + void cleanupTestCase(); private: @@ -107,6 +109,12 @@ void tst_rcc::initTestCase() QVERIFY(!m_dataPath.isEmpty()); } + +static inline bool isPythonComment(const QString &line) +{ + return line.startsWith(QLatin1Char('#')); +} + static QString doCompare(const QStringList &actual, const QStringList &expected, const QString &timeStampPath) { @@ -116,10 +124,13 @@ static QString doCompare(const QStringList &actual, const QStringList &expected, } QByteArray ba; + const bool isPython = isPythonComment(expected.constFirst()); for (int i = 0, n = expected.size(); i != n; ++i) { QString expectedLine = expected.at(i); if (expectedLine.startsWith("IGNORE:")) continue; + if (isPython && isPythonComment(expectedLine) && isPythonComment(actual.at(i))) + continue; if (expectedLine.startsWith("TIMESTAMP:")) { const QString relativePath = expectedLine.mid(strlen("TIMESTAMP:")); const QFileInfo fi(timeStampPath + QLatin1Char('/') + relativePath); @@ -405,6 +416,36 @@ void tst_rcc::readback() QCOMPARE(resourceData, fileSystemData); } +void tst_rcc::python() +{ + const QString path = m_dataPath + QLatin1String("/sizes"); + const QString testFileRoot = path + QLatin1String("/size-2-0-35-1"); + const QString qrcFile = testFileRoot + QLatin1String(".qrc"); + const QString expectedFile = testFileRoot + QLatin1String("_python.expected"); + const QString actualFile = testFileRoot + QLatin1String(".rcc"); + + QProcess process; + process.setWorkingDirectory(path); + process.start(m_rcc, { "-g", "python", "-o", actualFile, qrcFile}); + QVERIFY2(process.waitForStarted(), msgProcessStartFailed(process).constData()); + if (!process.waitForFinished()) { + process.kill(); + QFAIL(msgProcessTimeout(process).constData()); + } + QVERIFY2(process.exitStatus() == QProcess::NormalExit, + msgProcessCrashed(process).constData()); + QVERIFY2(process.exitCode() == 0, + msgProcessFailed(process).constData()); + + const auto actualLines = readLinesFromFile(actualFile, QString::KeepEmptyParts); + QVERIFY(!actualLines.isEmpty()); + const auto expectedLines = readLinesFromFile(expectedFile, QString::KeepEmptyParts); + QVERIFY(!expectedLines.isEmpty()); + const QString diff = doCompare(actualLines, expectedLines, path); + if (!diff.isEmpty()) + QFAIL(qPrintable(diff)); +} + void tst_rcc::cleanupTestCase() { QDir dataDir(m_dataPath + QLatin1String("/binary")); |