QtBase  v6.3.1
main.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2018 The Qt Company Ltd.
4 ** Copyright (C) 2018 Intel Corporation.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the tools applications of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
22 ** included in the packaging of this file. Please review the following
23 ** information to ensure the GNU General Public License requirements will
24 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25 **
26 ** $QT_END_LICENSE$
27 **
28 ****************************************************************************/
29 
30 #include <rcc.h>
31 
32 #include <qdebug.h>
33 #include <qdir.h>
34 #include <qfile.h>
35 #include <qfileinfo.h>
36 #include <qhashfunctions.h>
37 #include <qtextstream.h>
38 #include <qatomic.h>
39 #include <qglobal.h>
40 #include <qcoreapplication.h>
41 #include <qcommandlineoption.h>
42 #include <qcommandlineparser.h>
43 
44 #ifdef Q_OS_WIN
45 # include <fcntl.h>
46 # include <io.h>
47 # include <stdio.h>
48 #endif // Q_OS_WIN
49 
51 
53 {
54  const QFileInfoList entries = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot
56  for (const QFileInfo &entry : entries) {
57  if (entry.isDir()) {
58  dumpRecursive(entry.filePath(), out);
59  } else {
60  out << QLatin1String("<file>")
61  << entry.filePath()
62  << QLatin1String("</file>\n");
63  }
64  }
65 }
66 
67 int createProject(const QString &outFileName)
68 {
69  QDir currentDir = QDir::current();
70  QString currentDirName = currentDir.dirName();
71  if (currentDirName.isEmpty())
72  currentDirName = QLatin1String("root");
73 
74  QFile file;
75  bool isOk = false;
76  if (outFileName.isEmpty()) {
78  } else {
79  file.setFileName(outFileName);
81  }
82  if (!isOk) {
83  fprintf(stderr, "Unable to open %s: %s\n",
84  outFileName.isEmpty() ? qPrintable(outFileName) : "standard output",
85  qPrintable(file.errorString()));
86  return 1;
87  }
88 
90  out << QLatin1String("<!DOCTYPE RCC><RCC version=\"1.0\">\n"
91  "<qresource>\n");
92 
93  // use "." as dir to get relative file paths
95 
96  out << QLatin1String("</qresource>\n"
97  "</RCC>\n");
98 
99  return 0;
100 }
101 
102 // Escapes a path for use in a Depfile (Makefile syntax)
104 {
105  // Always use forward slashes
106  QString result = QDir::cleanPath(filepath);
107  // Spaces are escaped with a backslash
108  result.replace(QLatin1Char(' '), QLatin1String("\\ "));
109  // Pipes are escaped with a backslash
110  result.replace(QLatin1Char('|'), QLatin1String("\\|"));
111  // Dollars are escaped with a dollar
112  result.replace(QLatin1Char('$'), QLatin1String("$$"));
113 
114  return result;
115 }
116 
117 void writeDepFile(QIODevice &iodev, const QStringList &depsList, const QString &targetName)
118 {
119  QTextStream out(&iodev);
120  out << qPrintable(makefileEscape(targetName));
121  out << QLatin1Char(':');
122 
123  // Write depfile
124  for (int i = 0; i < depsList.size(); ++i) {
125  out << QLatin1Char(' ');
126 
127  out << qPrintable(makefileEscape(depsList.at(i)));
128  }
129 
130  out << QLatin1Char('\n');
131 }
132 
133 int runRcc(int argc, char *argv[])
134 {
135  QCoreApplication app(argc, argv);
137 
138  // Note that rcc isn't translated.
139  // If you use this code as an example for a translated app, make sure to translate the strings.
141  parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
142  parser.setApplicationDescription(QLatin1String("Qt Resource Compiler version " QT_VERSION_STR));
143  parser.addHelpOption();
144  parser.addVersionOption();
145 
146  QCommandLineOption outputOption(QStringList() << QStringLiteral("o") << QStringLiteral("output"));
147  outputOption.setDescription(QStringLiteral("Write output to <file> rather than stdout."));
148  outputOption.setValueName(QStringLiteral("file"));
149  parser.addOption(outputOption);
150 
151  QCommandLineOption tempOption(QStringList() << QStringLiteral("t") << QStringLiteral("temp"));
152  tempOption.setDescription(QStringLiteral("Use temporary <file> for big resources."));
153  tempOption.setValueName(QStringLiteral("file"));
154  parser.addOption(tempOption);
155 
156  QCommandLineOption nameOption(QStringLiteral("name"), QStringLiteral("Create an external initialization function with <name>."), QStringLiteral("name"));
157  parser.addOption(nameOption);
158 
159  QCommandLineOption rootOption(QStringLiteral("root"), QStringLiteral("Prefix resource access path with root path."), QStringLiteral("path"));
160  parser.addOption(rootOption);
161 
162 #if QT_CONFIG(zstd) && !defined(QT_NO_COMPRESS)
163 # define ALGOS "[zstd], zlib, none"
164 #elif QT_CONFIG(zstd)
165 # define ALGOS "[zstd], none"
166 #elif !defined(QT_NO_COMPRESS)
167 # define ALGOS "[zlib], none"
168 #else
169 # define ALGOS "[none]"
170 #endif
171  const QString &algoDescription =
172  QStringLiteral("Compress input files using algorithm <algo> (" ALGOS ").");
173  QCommandLineOption compressionAlgoOption(QStringLiteral("compress-algo"), algoDescription, QStringLiteral("algo"));
174  parser.addOption(compressionAlgoOption);
175 #undef ALGOS
176 
177  QCommandLineOption compressOption(QStringLiteral("compress"), QStringLiteral("Compress input files by <level>."), QStringLiteral("level"));
178  parser.addOption(compressOption);
179 
180  QCommandLineOption nocompressOption(QStringLiteral("no-compress"), QStringLiteral("Disable all compression. Same as --compress-algo=none."));
181  parser.addOption(nocompressOption);
182 
183  QCommandLineOption noZstdOption(QStringLiteral("no-zstd"), QStringLiteral("Disable usage of zstd compression."));
184  parser.addOption(noZstdOption);
185 
186  QCommandLineOption thresholdOption(QStringLiteral("threshold"), QStringLiteral("Threshold to consider compressing files."), QStringLiteral("level"));
187  parser.addOption(thresholdOption);
188 
189  QCommandLineOption binaryOption(QStringLiteral("binary"), QStringLiteral("Output a binary file for use as a dynamic resource."));
190  parser.addOption(binaryOption);
191 
192  QCommandLineOption generatorOption(QStringList{QStringLiteral("g"), QStringLiteral("generator")});
193  generatorOption.setDescription(QStringLiteral("Select generator."));
194  generatorOption.setValueName(QStringLiteral("cpp|python|python2"));
195  parser.addOption(generatorOption);
196 
197  QCommandLineOption passOption(QStringLiteral("pass"), QStringLiteral("Pass number for big resources"), QStringLiteral("number"));
198  parser.addOption(passOption);
199 
200  QCommandLineOption namespaceOption(QStringLiteral("namespace"), QStringLiteral("Turn off namespace macros."));
201  parser.addOption(namespaceOption);
202 
203  QCommandLineOption verboseOption(QStringLiteral("verbose"), QStringLiteral("Enable verbose mode."));
204  parser.addOption(verboseOption);
205 
206  QCommandLineOption listOption(QStringLiteral("list"), QStringLiteral("Only list .qrc file entries, do not generate code."));
207  parser.addOption(listOption);
208 
209  QCommandLineOption mapOption(QStringLiteral("list-mapping"),
210  QStringLiteral("Only output a mapping of resource paths to file system paths defined in the .qrc file, do not generate code."));
211  parser.addOption(mapOption);
212 
213  QCommandLineOption depFileOption(QStringList{QStringLiteral("d"), QStringLiteral("depfile")},
214  QStringLiteral("Write a depfile with the .qrc dependencies to <file>."), QStringLiteral("file"));
215  parser.addOption(depFileOption);
216 
217  QCommandLineOption projectOption(QStringLiteral("project"), QStringLiteral("Output a resource file containing all files from the current directory."));
218  parser.addOption(projectOption);
219 
220  QCommandLineOption formatVersionOption(QStringLiteral("format-version"), QStringLiteral("The RCC format version to write"), QStringLiteral("number"));
221  parser.addOption(formatVersionOption);
222 
223  parser.addPositionalArgument(QStringLiteral("inputs"), QStringLiteral("Input files (*.qrc)."));
224 
225 
226  //parse options
227  parser.process(app);
228 
230 
231  quint8 formatVersion = 3;
232  if (parser.isSet(formatVersionOption)) {
233  bool ok = false;
234  formatVersion = parser.value(formatVersionOption).toUInt(&ok);
235  if (!ok) {
236  errorMsg = QLatin1String("Invalid format version specified");
237  } else if (formatVersion < 1 || formatVersion > 3) {
238  errorMsg = QLatin1String("Unsupported format version specified");
239  }
240  }
241 
242  RCCResourceLibrary library(formatVersion);
243  if (parser.isSet(nameOption))
244  library.setInitName(parser.value(nameOption));
245  if (parser.isSet(rootOption)) {
246  library.setResourceRoot(QDir::cleanPath(parser.value(rootOption)));
247  if (library.resourceRoot().isEmpty()
248  || library.resourceRoot().at(0) != QLatin1Char('/'))
249  errorMsg = QLatin1String("Root must start with a /");
250  }
251 
252  if (parser.isSet(compressionAlgoOption))
253  library.setCompressionAlgorithm(RCCResourceLibrary::parseCompressionAlgorithm(parser.value(compressionAlgoOption), &errorMsg));
254  if (formatVersion < 3 && library.compressionAlgorithm() == RCCResourceLibrary::CompressionAlgorithm::Zstd)
255  errorMsg = QLatin1String("Zstandard compression requires format version 3 or higher");
256  if (parser.isSet(nocompressOption))
258  if (parser.isSet(noZstdOption))
259  library.setNoZstd(true);
260  if (parser.isSet(compressOption) && errorMsg.isEmpty()) {
261  int level = library.parseCompressionLevel(library.compressionAlgorithm(), parser.value(compressOption), &errorMsg);
262  library.setCompressLevel(level);
263  }
264  if (parser.isSet(thresholdOption))
265  library.setCompressThreshold(parser.value(thresholdOption).toInt());
266  if (parser.isSet(binaryOption))
268  if (parser.isSet(generatorOption)) {
269  auto value = parser.value(generatorOption);
270  if (value == QLatin1String("cpp")) {
272  } else if (value == QLatin1String("python")) {
274  } else if (value == QLatin1String("python2")) { // ### fixme Qt 7: remove
275  qWarning("Format python2 is no longer supported, defaulting to python.");
277  } else {
278  errorMsg = QLatin1String("Invalid generator: ") + value;
279  }
280  }
281 
282  if (parser.isSet(passOption)) {
283  if (parser.value(passOption) == QLatin1String("1"))
285  else if (parser.value(passOption) == QLatin1String("2"))
287  else
288  errorMsg = QLatin1String("Pass number must be 1 or 2");
289  }
290  if (parser.isSet(namespaceOption))
291  library.setUseNameSpace(!library.useNameSpace());
292  if (parser.isSet(verboseOption))
293  library.setVerbose(true);
294 
295  const bool list = parser.isSet(listOption);
296  const bool map = parser.isSet(mapOption);
297  const bool projectRequested = parser.isSet(projectOption);
298  const QStringList filenamesIn = parser.positionalArguments();
299 
300  for (const QString &file : filenamesIn) {
301  if (file == QLatin1String("-"))
302  continue;
303  else if (!QFile::exists(file)) {
304  qWarning("%s: File does not exist '%s'", argv[0], qPrintable(file));
305  return 1;
306  }
307  }
308 
309  QString outFilename = parser.value(outputOption);
310  QString tempFilename = parser.value(tempOption);
311  QString depFilename = parser.value(depFileOption);
312 
313  if (projectRequested) {
314  return createProject(outFilename);
315  }
316 
317  if (filenamesIn.isEmpty())
318  errorMsg = QStringLiteral("No input files specified.");
319 
320  if (!errorMsg.isEmpty()) {
321  fprintf(stderr, "%s: %s\n", argv[0], qPrintable(errorMsg));
322  parser.showHelp(1);
323  return 1;
324  }
325  QFile errorDevice;
326  errorDevice.open(stderr, QIODevice::WriteOnly|QIODevice::Text);
327 
328  if (library.verbose())
329  errorDevice.write("Qt resource compiler\n");
330 
331  library.setInputFiles(filenamesIn);
332 
333  if (!library.readFiles(list || map, errorDevice))
334  return 1;
335 
336  QFile out;
337 
338  // open output
339  QIODevice::OpenMode mode = QIODevice::NotOpen;
340  switch (library.format()) {
345  break;
349  break;
350  }
351 
352 
353  if (outFilename.isEmpty() || outFilename == QLatin1String("-")) {
354 #ifdef Q_OS_WIN
355  // Make sure fwrite to stdout doesn't do LF->CRLF
356  if (library.format() == RCCResourceLibrary::Binary)
357  _setmode(_fileno(stdout), _O_BINARY);
358  // Make sure QIODevice does not do LF->CRLF,
359  // otherwise we'll end up in CRCRLF instead of
360  // CRLF.
361  mode &= ~QIODevice::Text;
362 #endif // Q_OS_WIN
363  // using this overload close() only flushes.
364  out.open(stdout, mode);
365  } else {
366  out.setFileName(outFilename);
367  if (!out.open(mode)) {
368  const QString msg = QString::fromLatin1("Unable to open %1 for writing: %2\n")
369  .arg(outFilename, out.errorString());
370  errorDevice.write(msg.toUtf8());
371  return 1;
372  }
373  }
374 
375  // do the task
376  if (list) {
377  const QStringList data = library.dataFiles();
378  for (int i = 0; i < data.size(); ++i) {
379  out.write(qPrintable(QDir::cleanPath(data.at(i))));
380  out.write("\n");
381  }
382  return 0;
383  }
384 
385  if (map) {
386  const RCCResourceLibrary::ResourceDataFileMap data = library.resourceDataFileMap();
387  for (auto it = data.begin(), end = data.end(); it != end; ++it) {
388  out.write(qPrintable(it.key()));
389  out.write("\t");
390  out.write(qPrintable(QDir::cleanPath(it.value())));
391  out.write("\n");
392  }
393  return 0;
394  }
395 
396  // Write depfile
397  if (!depFilename.isEmpty()) {
398  QFile depout;
399  depout.setFileName(depFilename);
400 
401  if (outFilename.isEmpty() || outFilename == QLatin1String("-")) {
402  const QString msg = QString::fromUtf8("Unable to write depfile when outputting to stdout!\n");
403  errorDevice.write(msg.toUtf8());
404  return 1;
405  }
406 
407  if (!depout.open(QIODevice::WriteOnly | QIODevice::Text)) {
408  const QString msg = QString::fromUtf8("Unable to open depfile %1 for writing: %2\n")
409  .arg(depout.fileName(), depout.errorString());
410  errorDevice.write(msg.toUtf8());
411  return 1;
412  }
413 
414  writeDepFile(depout, library.dataFiles(), outFilename);
415  depout.close();
416  }
417 
418  QFile temp;
419  if (!tempFilename.isEmpty()) {
420  temp.setFileName(tempFilename);
421  if (!temp.open(QIODevice::ReadOnly)) {
422  const QString msg = QString::fromUtf8("Unable to open temporary file %1 for reading: %2\n")
423  .arg(tempFilename, out.errorString());
424  errorDevice.write(msg.toUtf8());
425  return 1;
426  }
427  }
428  bool success = library.output(out, temp, errorDevice);
429  if (!success) {
430  // erase the output file if we failed
431  out.remove();
432  return 1;
433  }
434  return 0;
435 }
436 
438 
439 int main(int argc, char *argv[])
440 {
441  // rcc uses a QHash to store files in the resource system.
442  // we must force a certain hash order when testing or tst_rcc will fail, see QTBUG-25078
443  // similar requirements exist for reproducibly builds.
445 
446  return QT_PREPEND_NAMESPACE(runRcc)(argc, argv);
447 }
small capitals from c petite p scientific i
[1]
Definition: afcover.h:80
const char msg[]
Definition: arch.cpp:46
#define value
[5]
FT_Library library
Definition: cffdrivr.c:660
The QCommandLineOption class defines a possible command-line option. \inmodule QtCore.
void setDescription(const QString &description)
void setValueName(const QString &name)
The QCommandLineParser class provides a means for handling the command line options.
The QCoreApplication class provides an event loop for Qt applications without UI.
static void setApplicationVersion(const QString &version)
The QDir class provides access to directory structures and their contents.
Definition: qdir.h:55
QString dirName() const
Definition: qdir.cpp:704
static QDir current()
Definition: qdir.h:244
static QString cleanPath(const QString &path)
Definition: qdir.cpp:2350
@ Files
Definition: qdir.h:58
@ NoSymLinks
Definition: qdir.h:60
@ NoDotAndDotDot
Definition: qdir.h:79
@ Dirs
Definition: qdir.h:57
void close() override
The QFile class provides an interface for reading from and writing to files.
Definition: qfile.h:94
bool open(OpenMode flags) override
Definition: qfile.cpp:897
void setFileName(const QString &name)
Definition: qfile.cpp:327
QString fileName() const override
Definition: qfile.cpp:302
bool exists() const
Definition: qfile.cpp:376
The QFileInfo class provides system-independent file information.
Definition: qfileinfo.h:57
The QIODevice class is the base interface class of all I/O devices in Qt.
Definition: qiodevice.h:70
qint64 write(const char *data, qint64 len)
Definition: qiodevice.cpp:1681
QString errorString() const
Definition: qiodevice.cpp:2169
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal.
Definition: qstring.h:84
The QString class provides a Unicode character string.
Definition: qstring.h:388
static QString fromLatin1(QByteArrayView ba)
Definition: qstring.cpp:5488
static QString fromUtf8(QByteArrayView utf8)
Definition: qstring.cpp:5632
bool isEmpty() const
Definition: qstring.h:1216
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=QLatin1Char(' ')) const
Definition: qstring.cpp:8318
The QStringList class provides a list of strings.
The QTextStream class provides a convenient interface for reading and writing text.
Definition: qtextstream.h:62
static CompressionAlgorithm parseCompressionAlgorithm(QStringView algo, QString *errorMsg)
Definition: rcc.cpp:869
int main(int argc, char **argv)
Definition: main.cpp:1
QMap< QString, QString > map
[6]
bool isOk(ResultWas::OfType resultType)
parser
Definition: devices.py:74
QList< QString > QStringList
Definition: qcontainerfwd.h:64
EGLOutputLayerEXT EGLint EGLAttrib value
unsigned char quint8
Definition: qglobal.h:284
#define qWarning
Definition: qlogging.h:179
GLenum mode
GLenum GLuint GLint level
GLuint GLuint end
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint entry
Definition: qopenglext.h:11002
GLuint64EXT * result
[6]
Definition: qopenglext.h:10932
int QT_PREPEND_NAMESPACE(QSharedMemoryPrivate)
#define QStringLiteral(str)
QT_BEGIN_NAMESPACE void dumpRecursive(const QDir &dir, QTextStream &out)
Definition: main.cpp:52
#define ALGOS
int runRcc(int argc, char *argv[])
Definition: main.cpp:133
void writeDepFile(QIODevice &iodev, const QStringList &depsList, const QString &targetName)
Definition: main.cpp:117
QString makefileEscape(const QString &filepath)
Definition: main.cpp:103
int createProject(const QString &outFileName)
Definition: main.cpp:67
QFile file
[0]
QTextStream out(stdout)
[7]
QString dir
[11]
QApplication app(argc, argv)
[0]
QStringList::Iterator it
QStringList list
[0]
static Q_CORE_EXPORT void setDeterministicGlobalSeed()
Definition: qhash.cpp:902
The QLatin1Char class provides an 8-bit ASCII/Latin-1 character.
Definition: qchar.h:53
const char errorMsg[]