Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// Copyright (C) 2018 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
4// Qt-Security score:insignificant reason:build-tool
5
6#include <rcc.h>
7
8#include <qdebug.h>
9#include <qdir.h>
10#include <qfile.h>
11#include <qfileinfo.h>
12#include <qhashfunctions.h>
13#include <qtextstream.h>
14#include <qatomic.h>
15#include <qglobal.h>
16#include <qcoreapplication.h>
17#include <qcommandlineoption.h>
18#include <qcommandlineparser.h>
19
20#ifdef Q_OS_WIN
21# include <fcntl.h>
22# include <io.h>
23# include <stdio.h>
24#endif // Q_OS_WIN
25
27
28using namespace Qt::StringLiterals;
29
30void dumpRecursive(const QDir &dir, QTextStream &out)
31{
32 const QFileInfoList entries = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot
33 | QDir::NoSymLinks);
34 for (const QFileInfo &entry : entries) {
35 if (entry.isDir()) {
36 dumpRecursive(entry.filePath(), out);
37 } else {
38 out << "<file>"_L1
39 << entry.filePath()
40 << "</file>\n"_L1;
41 }
42 }
43}
44
45int createProject(const QString &outFileName)
46{
47 QDir currentDir = QDir::current();
48 QString currentDirName = currentDir.dirName();
49 if (currentDirName.isEmpty())
50 currentDirName = "root"_L1;
51
52 QFile file;
53 bool isOk = false;
54 if (outFileName.isEmpty()) {
55 isOk = file.open(stdout, QFile::WriteOnly | QFile::Text);
56 } else {
57 file.setFileName(outFileName);
58 isOk = file.open(QFile::WriteOnly | QFile::Text);
59 }
60 if (!isOk) {
61 fprintf(stderr, "Unable to open %s: %s\n",
62 outFileName.isEmpty() ? qPrintable(outFileName) : "standard output",
63 qPrintable(file.errorString()));
64 return 1;
65 }
66
67 QTextStream out(&file);
68 out << "<!DOCTYPE RCC><RCC version=\"1.0\">\n"
69 "<qresource>\n"_L1;
70
71 // use "." as dir to get relative file paths
72 dumpRecursive(QDir("."_L1), out);
73
74 out << "</qresource>\n"
75 "</RCC>\n"_L1;
76
77 return 0;
78}
79
80// Escapes a path for use in a Depfile (Makefile syntax)
81QString makefileEscape(const QString &filepath)
82{
83 // Always use forward slashes
84 QString result = QDir::cleanPath(filepath);
85 // Spaces are escaped with a backslash
86 result.replace(u' ', "\\ "_L1);
87 // Pipes are escaped with a backslash
88 result.replace(u'|', "\\|"_L1);
89 // Dollars are escaped with a dollar
90 result.replace(u'$', "$$"_L1);
91
92 return result;
93}
94
95void writeDepFile(QIODevice &iodev, const QStringList &depsList, const QString &targetName)
96{
97 QTextStream out(&iodev);
98 out << qPrintable(makefileEscape(targetName));
99 out << QChar(u':');
100
101 // Write depfile
102 for (int i = 0; i < depsList.size(); ++i) {
103 out << QChar(u' ');
104
105 out << qPrintable(makefileEscape(depsList.at(i)));
106 }
107
108 out << QChar(u'\n');
109}
110
111int runRcc(int argc, char *argv[])
112{
113 QCoreApplication app(argc, argv);
114 QCoreApplication::setApplicationVersion(QStringLiteral(QT_VERSION_STR));
115
116 // Note that rcc isn't translated.
117 // If you use this code as an example for a translated app, make sure to translate the strings.
118 QCommandLineParser parser;
119 parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
120 parser.setApplicationDescription("Qt Resource Compiler version " QT_VERSION_STR ""_L1);
121 parser.addHelpOption();
122 parser.addVersionOption();
123
124 QCommandLineOption outputOption(QStringList() << QStringLiteral("o") << QStringLiteral("output"));
125 outputOption.setDescription(QStringLiteral("Write output to <file> rather than stdout."));
126 outputOption.setValueName(QStringLiteral("file"));
127 parser.addOption(outputOption);
128
129 QCommandLineOption tempOption(QStringList() << QStringLiteral("t") << QStringLiteral("temp"));
130 tempOption.setDescription(QStringLiteral("Use temporary <file> for big resources."));
131 tempOption.setValueName(QStringLiteral("file"));
132 parser.addOption(tempOption);
133
134 QCommandLineOption nameOption(QStringLiteral("name"), QStringLiteral("Create an external initialization function with <name>."), QStringLiteral("name"));
135 parser.addOption(nameOption);
136
137 QCommandLineOption rootOption(QStringLiteral("root"), QStringLiteral("Prefix resource access path with root path."), QStringLiteral("path"));
138 parser.addOption(rootOption);
139
140#if QT_CONFIG(zstd) && !defined(QT_NO_COMPRESS)
141# define ALGOS "[zstd], zlib, none"
142#elif QT_CONFIG(zstd)
143# define ALGOS "[zstd], none"
144#elif !defined(QT_NO_COMPRESS)
145# define ALGOS "[zlib], none"
146#else
147# define ALGOS "[none]"
148#endif
149 const QString &algoDescription =
150 QStringLiteral("Compress input files using algorithm <algo> (" ALGOS ").");
151 QCommandLineOption compressionAlgoOption(QStringLiteral("compress-algo"), algoDescription, QStringLiteral("algo"));
152 parser.addOption(compressionAlgoOption);
153#undef ALGOS
154
155 QCommandLineOption compressOption(QStringLiteral("compress"), QStringLiteral("Compress input files by <level>."), QStringLiteral("level"));
156 parser.addOption(compressOption);
157
158 QCommandLineOption nocompressOption(QStringLiteral("no-compress"), QStringLiteral("Disable all compression. Same as --compress-algo=none."));
159 parser.addOption(nocompressOption);
160
161 QCommandLineOption noZstdOption(QStringLiteral("no-zstd"), QStringLiteral("Disable usage of zstd compression."));
162 parser.addOption(noZstdOption);
163
164 QCommandLineOption thresholdOption(QStringLiteral("threshold"), QStringLiteral("Threshold to consider compressing files."), QStringLiteral("level"));
165 parser.addOption(thresholdOption);
166
167 QCommandLineOption binaryOption(QStringLiteral("binary"), QStringLiteral("Output a binary file for use as a dynamic resource."));
168 parser.addOption(binaryOption);
169
170 QCommandLineOption generatorOption(QStringList{QStringLiteral("g"), QStringLiteral("generator")});
171 generatorOption.setDescription(QStringLiteral("Select generator."));
172 generatorOption.setValueName(QStringLiteral("cpp|python|python2"));
173 parser.addOption(generatorOption);
174
175 QCommandLineOption passOption(QStringLiteral("pass"), QStringLiteral("Pass number for big resources"), QStringLiteral("number"));
176 parser.addOption(passOption);
177
178 QCommandLineOption namespaceOption(QStringLiteral("namespace"), QStringLiteral("Turn off namespace macros."));
179 parser.addOption(namespaceOption);
180
181 QCommandLineOption verboseOption(QStringLiteral("verbose"), QStringLiteral("Enable verbose mode."));
182 parser.addOption(verboseOption);
183
184 QCommandLineOption listOption(QStringLiteral("list"), QStringLiteral("Only list .qrc file entries, do not generate code."));
185 parser.addOption(listOption);
186
187 QCommandLineOption mapOption(QStringLiteral("list-mapping"),
188 QStringLiteral("Only output a mapping of resource paths to file system paths defined in the .qrc file, do not generate code."));
189 parser.addOption(mapOption);
190
191 QCommandLineOption depFileOption(QStringList{QStringLiteral("d"), QStringLiteral("depfile")},
192 QStringLiteral("Write a depfile with the .qrc dependencies to <file>."), QStringLiteral("file"));
193 parser.addOption(depFileOption);
194
195 QCommandLineOption projectOption(QStringLiteral("project"), QStringLiteral("Output a resource file containing all files from the current directory."));
196 parser.addOption(projectOption);
197
198 QCommandLineOption formatVersionOption(QStringLiteral("format-version"), QStringLiteral("The RCC format version to write"), QStringLiteral("number"));
199 parser.addOption(formatVersionOption);
200
201 parser.addPositionalArgument(QStringLiteral("inputs"), QStringLiteral("Input files (*.qrc)."));
202
203
204 //parse options
205 parser.process(app);
206
207 QString errorMsg;
208
209 quint8 formatVersion = 3;
210 if (parser.isSet(formatVersionOption)) {
211 bool ok = false;
212 formatVersion = parser.value(formatVersionOption).toUInt(&ok);
213 if (!ok) {
214 errorMsg = "Invalid format version specified"_L1;
215 } else if (formatVersion < 1 || formatVersion > 3) {
216 errorMsg = "Unsupported format version specified"_L1;
217 }
218 }
219
220 RCCResourceLibrary library(formatVersion);
221 if (parser.isSet(nameOption))
222 library.setInitName(parser.value(nameOption));
223 if (parser.isSet(rootOption)) {
224 library.setResourceRoot(QDir::cleanPath(parser.value(rootOption)));
225 if (library.resourceRoot().isEmpty() || library.resourceRoot().at(0) != u'/')
226 errorMsg = "Root must start with a /"_L1;
227 }
228
229 if (parser.isSet(compressionAlgoOption))
230 library.setCompressionAlgorithm(RCCResourceLibrary::parseCompressionAlgorithm(parser.value(compressionAlgoOption), &errorMsg));
231 if (parser.isSet(noZstdOption))
232 library.setNoZstd(true);
234 if (formatVersion < 3)
235 errorMsg = "Zstandard compression requires format version 3 or higher"_L1;
236 if (library.noZstd())
237 errorMsg = "--compression-algo=zstd and --no-zstd both specified."_L1;
238 }
239 if (parser.isSet(nocompressOption))
241 if (parser.isSet(compressOption) && errorMsg.isEmpty()) {
242 int level = library.parseCompressionLevel(library.compressionAlgorithm(), parser.value(compressOption), &errorMsg);
243 library.setCompressLevel(level);
244 }
245 if (parser.isSet(thresholdOption))
246 library.setCompressThreshold(parser.value(thresholdOption).toInt());
247 if (parser.isSet(binaryOption))
249 if (parser.isSet(generatorOption)) {
250 auto value = parser.value(generatorOption);
251 if (value == "cpp"_L1) {
253 } else if (value == "python"_L1) {
255 } else if (value == "python2"_L1) { // ### fixme Qt 7: remove
256 qWarning("Format python2 is no longer supported, defaulting to python.");
258 } else {
259 errorMsg = "Invalid generator: "_L1 + value;
260 }
261 }
262
263 if (parser.isSet(passOption)) {
264 if (parser.value(passOption) == "1"_L1)
266 else if (parser.value(passOption) == "2"_L1)
268 else
269 errorMsg = "Pass number must be 1 or 2"_L1;
270 }
271 if (parser.isSet(namespaceOption))
273 if (parser.isSet(verboseOption))
274 library.setVerbose(true);
275
276 const bool list = parser.isSet(listOption);
277 const bool map = parser.isSet(mapOption);
278 const bool projectRequested = parser.isSet(projectOption);
279 const QStringList filenamesIn = parser.positionalArguments();
280
281 for (const QString &file : filenamesIn) {
282 if (file == "-"_L1)
283 continue;
284 else if (!QFile::exists(file)) {
285 qWarning("%s: File does not exist '%s'", argv[0], qPrintable(file));
286 return 1;
287 }
288 }
289
290 QString outFilename = parser.value(outputOption);
291 QString tempFilename = parser.value(tempOption);
292 QString depFilename = parser.value(depFileOption);
293
294 if (projectRequested) {
295 return createProject(outFilename);
296 }
297
298 if (filenamesIn.isEmpty())
299 errorMsg = QStringLiteral("No input files specified.");
300
301 if (!errorMsg.isEmpty()) {
302 fprintf(stderr, "%s: %s\n", argv[0], qPrintable(errorMsg));
303 parser.showHelp(1);
304 return 1;
305 }
306 QFile errorDevice;
307 if (!errorDevice.open(stderr, QIODevice::WriteOnly|QIODevice::Text))
308 return 1;
309
310 if (library.verbose())
311 errorDevice.write("Qt resource compiler\n");
312
313 library.setInputFiles(filenamesIn);
314
315 if (!library.readFiles(list || map, errorDevice))
316 return 1;
317
318 QFile out;
319
320 // open output
321 QIODevice::OpenMode mode = QIODevice::NotOpen;
322 switch (library.format()) {
323 case RCCResourceLibrary::C_Code:
324 case RCCResourceLibrary::Pass1:
325 case RCCResourceLibrary::Python_Code:
326 mode = QIODevice::WriteOnly | QIODevice::Text;
327 break;
328 case RCCResourceLibrary::Pass2:
329 case RCCResourceLibrary::Binary:
330 mode = QIODevice::WriteOnly;
331 break;
332 }
333
334
335 if (outFilename.isEmpty() || outFilename == "-"_L1) {
336#ifdef Q_OS_WIN
337 // Make sure fwrite to stdout doesn't do LF->CRLF
338 if (library.format() == RCCResourceLibrary::Binary)
339 _setmode(_fileno(stdout), _O_BINARY);
340 // Make sure QIODevice does not do LF->CRLF,
341 // otherwise we'll end up in CRCRLF instead of
342 // CRLF.
343 mode &= ~QIODevice::Text;
344#endif // Q_OS_WIN
345 // using this overload close() only flushes.
346 if (!out.open(stdout, mode)) {
347 const QString msg = QString::fromLatin1("Unable to open standard output for writing: %1\n")
348 .arg(out.errorString());
349 errorDevice.write(msg.toUtf8());
350 return 1;
351 }
352 } else {
353 out.setFileName(outFilename);
354 if (!out.open(mode)) {
355 const QString msg = QString::fromLatin1("Unable to open %1 for writing: %2\n")
356 .arg(outFilename, out.errorString());
357 errorDevice.write(msg.toUtf8());
358 return 1;
359 }
360 }
361
362 // do the task
363 if (list) {
364 const QStringList data = library.dataFiles();
365 for (int i = 0; i < data.size(); ++i) {
366 out.write(qPrintable(QDir::cleanPath(data.at(i))));
367 out.write("\n");
368 }
369 return 0;
370 }
371
372 if (map) {
373 const RCCResourceLibrary::ResourceDataFileMap data = library.resourceDataFileMap();
374 for (auto it = data.begin(), end = data.end(); it != end; ++it) {
375 out.write(qPrintable(it.key()));
376 out.write("\t");
377 out.write(qPrintable(QDir::cleanPath(it.value())));
378 out.write("\n");
379 }
380 return 0;
381 }
382
383 // Write depfile
384 if (!depFilename.isEmpty()) {
385 QFile depout;
386 depout.setFileName(depFilename);
387
388 if (outFilename.isEmpty() || outFilename == "-"_L1) {
389 const QString msg = QString::fromUtf8("Unable to write depfile when outputting to stdout!\n");
390 errorDevice.write(msg.toUtf8());
391 return 1;
392 }
393
394 if (!depout.open(QIODevice::WriteOnly | QIODevice::Text)) {
395 const QString msg = QString::fromUtf8("Unable to open depfile %1 for writing: %2\n")
396 .arg(depout.fileName(), depout.errorString());
397 errorDevice.write(msg.toUtf8());
398 return 1;
399 }
400
401 writeDepFile(depout, library.dataFiles(), outFilename);
402 depout.close();
403 }
404
405 QFile temp;
406 if (!tempFilename.isEmpty()) {
407 temp.setFileName(tempFilename);
408 if (!temp.open(QIODevice::ReadOnly)) {
409 const QString msg = QString::fromUtf8("Unable to open temporary file %1 for reading: %2\n")
410 .arg(tempFilename, out.errorString());
411 errorDevice.write(msg.toUtf8());
412 return 1;
413 }
414 }
415 bool success = library.output(out, temp, errorDevice);
416 if (!success) {
417 // erase the output file if we failed
418 out.remove();
419 return 1;
420 }
421 return 0;
422}
423
425
426int main(int argc, char *argv[])
427{
428 // rcc uses a QHash to store files in the resource system.
429 // we must force a certain hash order when testing or tst_rcc will fail, see QTBUG-25078
430 // similar requirements exist for reproducibly builds.
431 QHashSeed::setDeterministicGlobalSeed();
432
433 return QT_PREPEND_NAMESPACE(runRcc)(argc, argv);
434}
The QCommandLineOption class defines a possible command-line option. \inmodule QtCore.
The QCommandLineParser class provides a means for handling the command line options.
Definition qfile.h:71
bool readFiles(bool listMode, QIODevice &errorDevice)
Definition rcc.cpp:837
Format format() const
Definition rcc.h:37
void setNoZstd(bool v)
Definition rcc.h:86
void setResourceRoot(const QString &root)
Definition rcc.h:76
static int parseCompressionLevel(CompressionAlgorithm algo, const QString &level, QString *errorMsg)
Definition rcc.cpp:947
bool useNameSpace() const
Definition rcc.h:80
bool output(QIODevice &outDevice, QIODevice &tempDevice, QIODevice &errorDevice)
Definition rcc.cpp:973
void setVerbose(bool b)
Definition rcc.h:48
void setInitName(const QString &name)
Definition rcc.h:51
void setCompressionAlgorithm(CompressionAlgorithm algo)
Definition rcc.h:66
void setCompressThreshold(int t)
Definition rcc.h:73
CompressionAlgorithm compressionAlgorithm() const
Definition rcc.h:67
void setCompressLevel(int c)
Definition rcc.h:70
void setFormat(Format f)
Definition rcc.h:36
QHash< QString, QString > ResourceDataFileMap
Definition rcc.h:45
void setUseNameSpace(bool v)
Definition rcc.h:79
Combined button and popup list for selecting options.
const QString & asString(const QString &s)
Definition qstring.h:1700
QList< QFileInfo > QFileInfoList
Definition qfileinfo.h:195
#define qPrintable(string)
Definition qstring.h:1705
#define QStringLiteral(str)
Definition qstring.h:1847
#define ALGOS
void dumpRecursive(const QDir &dir, QTextStream &out)
Definition main.cpp:30
int runRcc(int argc, char *argv[])
Definition main.cpp:111
void writeDepFile(QIODevice &iodev, const QStringList &depsList, const QString &targetName)
Definition main.cpp:95
QString makefileEscape(const QString &filepath)
Definition main.cpp:81
int createProject(const QString &outFileName)
Definition main.cpp:45
int main(int argc, char *argv[])
[ctor_close]