rcc: Add -d option to output a Makefile-syntax depfile

The -d option makes rcc output a dependency file with the specified
file name.

The resulting dependency file is useful for make or ninja based build
systems.

[ChangeLog][Tools][rcc] Added -d option to generate a dependency file.

Fixes: QTBUG-45460
Change-Id: I495ade50f8d9865d4c00dce9373b2b6d1a6c8f2f
Reviewed-by: Jörg Bornemann <joerg.bornemann@qt.io>
bb10
Marvin Scholz 2018-10-28 01:52:15 +02:00
parent 9520ba5a73
commit a03270f891
7 changed files with 121 additions and 0 deletions

View File

@ -99,6 +99,37 @@ int createProject(const QString &outFileName)
return 0;
}
// Escapes a path for use in a Depfile (Makefile syntax)
QString makefileEscape(const QString &filepath)
{
// Always use forward slashes
QString result = QDir::cleanPath(filepath);
// Spaces are escaped with a backslash
result.replace(QLatin1Char(' '), QLatin1String("\\ "));
// Pipes are escaped with a backslash
result.replace(QLatin1Char('|'), QLatin1String("\\|"));
// Dollars are escaped with a dollar
result.replace(QLatin1Char('$'), QLatin1String("$$"));
return result;
}
void writeDepFile(QIODevice &iodev, const QStringList &depsList, const QString &targetName)
{
QTextStream out(&iodev);
out << qPrintable(makefileEscape(targetName));
out << QLatin1Char(':');
// Write depfile
for (int i = 0; i < depsList.size(); ++i) {
out << QLatin1Char(' ');
out << qPrintable(makefileEscape(depsList.at(i)));
}
out << QLatin1Char('\n');
}
int runRcc(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
@ -176,6 +207,10 @@ int runRcc(int argc, char *argv[])
QStringLiteral("Only output a mapping of resource paths to file system paths defined in the .qrc file, do not generate code."));
parser.addOption(mapOption);
QCommandLineOption depFileOption(QStringList{QStringLiteral("d"), QStringLiteral("depfile")},
QStringLiteral("Write a depfile with the .qrc dependencies to <file>."), QStringLiteral("file"));
parser.addOption(depFileOption);
QCommandLineOption projectOption(QStringLiteral("project"), QStringLiteral("Output a resource file containing all files from the current directory."));
parser.addOption(projectOption);
@ -266,6 +301,7 @@ int runRcc(int argc, char *argv[])
QString outFilename = parser.value(outputOption);
QString tempFilename = parser.value(tempOption);
QString depFilename = parser.value(depFileOption);
if (projectRequested) {
return createProject(outFilename);
@ -352,6 +388,28 @@ int runRcc(int argc, char *argv[])
return 0;
}
// Write depfile
if (!depFilename.isEmpty()) {
QFile depout;
depout.setFileName(depFilename);
if (outFilename.isEmpty() || outFilename == QLatin1String("-")) {
const QString msg = QString::fromUtf8("Unable to write depfile when outputting to stdout!\n");
errorDevice.write(msg.toUtf8());
return 1;
}
if (!depout.open(QIODevice::WriteOnly | QIODevice::Text)) {
const QString msg = QString::fromUtf8("Unable to open depfile %1 for writing: %2\n")
.arg(depout.fileName(), depout.errorString());
errorDevice.write(msg.toUtf8());
return 1;
}
writeDepFile(depout, library.dataFiles(), outFilename);
depout.close();
}
QFile temp;
if (!tempFilename.isEmpty()) {
temp.setFileName(tempFilename);

View File

@ -0,0 +1 @@
simple.qrc.cpp: ../images/images/circle.png ../images/images/square.png

View File

@ -0,0 +1,6 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>../images/images/circle.png</file>
<file>../images/images/square.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1 @@
ABC

View File

@ -0,0 +1 @@
specialchar.qrc.cpp: specialchar$$file.txt ../images/images/subdir/triangle.png

View File

@ -0,0 +1,6 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>../images/images/subdir/triangle.png</file>
<file>specialchar$file.txt</file>
</qresource>
</RCC>

View File

@ -89,6 +89,9 @@ private slots:
void readback_data();
void readback();
void depFileGeneration_data();
void depFileGeneration();
void python();
void cleanupTestCase();
@ -416,6 +419,49 @@ void tst_rcc::readback()
QCOMPARE(resourceData, fileSystemData);
}
void tst_rcc::depFileGeneration_data()
{
QTest::addColumn<QString>("qrcfile");
QTest::addColumn<QString>("depfile");
QTest::addColumn<QString>("expected");
QTest::newRow("simple") << "simple.qrc" << "simple.d" << "simple.d.expected";
QTest::newRow("specialchar") << "specialchar.qrc" << "specialchar.d" << "specialchar.d.expected";
}
void tst_rcc::depFileGeneration()
{
QFETCH(QString, qrcfile);
QFETCH(QString, depfile);
QFETCH(QString, expected);
const QString directory = m_dataPath + QLatin1String("/depfile");
QProcess process;
process.setWorkingDirectory(directory);
process.start(m_rcc, { "-d", depfile, "-o", qrcfile + ".cpp", 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());
QFile depFileOutput(directory + QLatin1String("/") + depfile);
QVERIFY(depFileOutput.open(QIODevice::ReadOnly | QIODevice::Text));
QByteArray depFileData = depFileOutput.readAll();
depFileOutput.close();
QFile depFileExpected(directory + QLatin1String("/") + expected);
QVERIFY(depFileExpected.open(QIODevice::ReadOnly | QIODevice::Text));
QByteArray expectedData = depFileExpected.readAll();
depFileExpected.close();
QCOMPARE(depFileData, expectedData);
}
void tst_rcc::python()
{
const QString path = m_dataPath + QLatin1String("/sizes");
@ -450,6 +496,8 @@ void tst_rcc::cleanupTestCase()
{
QDir dataDir(m_dataPath + QLatin1String("/binary"));
QFileInfoList entries = dataDir.entryInfoList(QStringList() << QLatin1String("*.rcc"));
QDir dataDepDir(m_dataPath + QLatin1String("/depfile"));
entries += dataDepDir.entryInfoList({QLatin1String("*.d"), QLatin1String("*.qrc.cpp")});
foreach (const QFileInfo &entry, entries)
QFile::remove(entry.absoluteFilePath());
}