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) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
4
5#include <depfile_shared.h>
6#include "preprocessor.h"
7#include "moc.h"
9#include "collectjson.h"
10
11#include <qfile.h>
12#include <qfileinfo.h>
13#include <qdir.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <ctype.h>
17#include <errno.h>
18
19#include <qcoreapplication.h>
20#include <qcommandlineoption.h>
21#include <qcommandlineparser.h>
22
23#include <memory>
24
26
27using namespace Qt::StringLiterals;
28
29/*
30 This function looks at two file names and returns the name of the
31 infile with a path relative to outfile.
32
33 Examples:
34
35 /tmp/abc, /tmp/bcd -> abc
36 xyz/a/bc, xyz/b/ac -> ../a/bc
37 /tmp/abc, xyz/klm -> /tmp/abc
38 */
39
40static QByteArray combinePath(const QString &infile, const QString &outfile)
41{
42 QFileInfo inFileInfo(QDir::current(), infile);
43 QFileInfo outFileInfo(QDir::current(), outfile);
44 const QByteArray relativePath = QFile::encodeName(outFileInfo.dir().relativeFilePath(inFileInfo.filePath()));
45#ifdef Q_OS_WIN
46 // It's a system limitation.
47 // It depends on the Win API function which is used by the program to open files.
48 // cl apparently uses the functions that have the MAX_PATH limitation.
49 if (outFileInfo.dir().absolutePath().length() + relativePath.length() + 1 >= 260)
50 return QFile::encodeName(inFileInfo.absoluteFilePath());
51#endif
52 return relativePath;
53}
54
55
56void error(const char *msg = "Invalid argument")
57{
58 if (msg)
59 fprintf(stderr, "moc: %s\n", msg);
60}
61
62static auto openFileForWriting(const QString &name)
63{
64 struct Closer { void operator()(FILE *handle) const { fclose(handle); } };
65 using R = std::unique_ptr<FILE, Closer>;
66
67#ifdef _MSC_VER
68 FILE *file;
69 if (_wfopen_s(&file, reinterpret_cast<const wchar_t *>(name.utf16()), L"w") != 0)
70 return R{};
71 return R{file};
72#else
73 return R{fopen(QFile::encodeName(name).constData(), "w")};
74#endif
75}
76using File = decltype(openFileForWriting({}));
77
78static inline bool hasNext(const Symbols &symbols, int i)
79{ return (i < symbols.size()); }
80
81static inline const Symbol &next(const Symbols &symbols, int &i)
82{ return symbols.at(i++); }
83
84
86 QByteArray output;
87 int lineNum = 1;
88 Token last = PP_NOTOKEN;
89 Token secondlast = last;
90 int i = 0;
91 while (hasNext(symbols, i)) {
92 Symbol sym = next(symbols, i);
93 switch (sym.token) {
94 case PP_NEWLINE:
95 case PP_WHITESPACE:
96 if (last != PP_WHITESPACE) {
97 secondlast = last;
98 last = PP_WHITESPACE;
99 output += ' ';
100 }
101 continue;
102 case PP_STRING_LITERAL:
103 if (last == PP_STRING_LITERAL)
104 output.chop(1);
105 else if (secondlast == PP_STRING_LITERAL && last == PP_WHITESPACE)
106 output.chop(2);
107 else
108 break;
109 output += sym.lexemView().mid(1);
110 secondlast = last;
111 last = PP_STRING_LITERAL;
112 continue;
113 case MOC_INCLUDE_BEGIN:
114 lineNum = 0;
115 continue;
116 case MOC_INCLUDE_END:
117 lineNum = sym.lineNum;
118 continue;
119 default:
120 break;
121 }
122 secondlast = last;
123 last = sym.token;
124
125 const int padding = sym.lineNum - lineNum;
126 if (padding > 0) {
127 output.resize(output.size() + padding);
128 memset(output.data() + output.size() - padding, '\n', padding);
129 lineNum = sym.lineNum;
130 }
131
132 output += sym.lexem();
133 }
134
135 return output;
136}
137
138static QStringList argumentsFromCommandLineAndFile(const QStringList &arguments, bool &hasOptionFiles)
139{
140 QStringList allArguments;
141 hasOptionFiles = false;
142 allArguments.reserve(arguments.size());
143 for (const QString &argument : arguments) {
144 // "@file" doesn't start with a '-' so we can't use QCommandLineParser for it
145 if (argument.startsWith(u'@')) {
146 QString optionsFile = argument;
147 optionsFile.remove(0, 1);
148 if (optionsFile.isEmpty()) {
149 error("The @ option requires an input file");
150 return QStringList();
151 }
152 QFile f(optionsFile);
153 if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
154 error("Cannot open options file specified with @");
155 return QStringList();
156 }
157 hasOptionFiles = true;
158 while (!f.atEnd()) {
159 QString line = QString::fromLocal8Bit(f.readLine().trimmed());
160 if (!line.isEmpty())
161 allArguments << line;
162 }
163 } else {
164 allArguments << argument;
165 }
166 }
167 return allArguments;
168}
169
170int runMoc(int argc, char **argv)
171{
172 QCoreApplication app(argc, argv);
173 QCoreApplication::setApplicationVersion(QString::fromLatin1(QT_VERSION_STR));
174 // let moc identify itself as moc, even if the binary has been renamed
175 QCoreApplication::setApplicationName(QString::fromLatin1("moc"));
176
177 bool autoInclude = true;
178 bool defaultInclude = true;
179 Preprocessor pp;
180 Moc moc;
181 pp.macros["Q_MOC_RUN"];
182 pp.macros["__cplusplus"];
183
184 // Don't stumble over GCC extensions
185 Macro dummyVariadicFunctionMacro;
186 dummyVariadicFunctionMacro.isFunction = true;
187 dummyVariadicFunctionMacro.isVariadic = true;
188 dummyVariadicFunctionMacro.arguments += Symbol(0, PP_IDENTIFIER, "__VA_ARGS__");
189 pp.macros["__attribute__"] = dummyVariadicFunctionMacro;
190 pp.macros["__declspec"] = dummyVariadicFunctionMacro;
191
192 QString filename;
193 QString output;
194 QFile in;
195 File out;
196
197 // Note that moc isn't translated.
198 // If you use this code as an example for a translated app, make sure to translate the strings.
199 QCommandLineParser parser;
200 parser.setApplicationDescription(QStringLiteral("Qt Meta Object Compiler version %1 (Qt %2)")
201 .arg(mocOutputRevision).arg(QString::fromLatin1(QT_VERSION_STR)));
202 parser.addHelpOption();
203 parser.addVersionOption();
204 parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
205
206 QCommandLineOption outputOption(QStringLiteral("o"));
207 outputOption.setDescription(QStringLiteral("Write output to file rather than stdout."));
208 outputOption.setValueName(QStringLiteral("file"));
209 outputOption.setFlags(QCommandLineOption::ShortOptionStyle);
210 parser.addOption(outputOption);
211
212 QCommandLineOption includePathOption(QStringLiteral("I"));
213 includePathOption.setDescription(QStringLiteral("Add dir to the include path for header files."));
214 includePathOption.setValueName(QStringLiteral("dir"));
215 includePathOption.setFlags(QCommandLineOption::ShortOptionStyle);
216 parser.addOption(includePathOption);
217
218 QCommandLineOption macFrameworkOption(QStringLiteral("F"));
219 macFrameworkOption.setDescription(QStringLiteral("Add Mac framework to the include path for header files."));
220 macFrameworkOption.setValueName(QStringLiteral("framework"));
221 macFrameworkOption.setFlags(QCommandLineOption::ShortOptionStyle);
222 parser.addOption(macFrameworkOption);
223
224 QCommandLineOption preprocessOption(QStringLiteral("E"));
225 preprocessOption.setDescription(QStringLiteral("Preprocess only; do not generate meta object code."));
226 parser.addOption(preprocessOption);
227
228 QCommandLineOption defineOption(QStringLiteral("D"));
229 defineOption.setDescription(QStringLiteral("Define macro, with optional definition."));
230 defineOption.setValueName(QStringLiteral("macro[=def]"));
231 defineOption.setFlags(QCommandLineOption::ShortOptionStyle);
232 parser.addOption(defineOption);
233
234 QCommandLineOption undefineOption(QStringLiteral("U"));
235 undefineOption.setDescription(QStringLiteral("Undefine macro."));
236 undefineOption.setValueName(QStringLiteral("macro"));
237 undefineOption.setFlags(QCommandLineOption::ShortOptionStyle);
238 parser.addOption(undefineOption);
239
240 QCommandLineOption metadataOption(QStringLiteral("M"));
241 metadataOption.setDescription(QStringLiteral("Add key/value pair to plugin meta data"));
242 metadataOption.setValueName(QStringLiteral("key=value"));
243 metadataOption.setFlags(QCommandLineOption::ShortOptionStyle);
244 parser.addOption(metadataOption);
245
246 QCommandLineOption compilerFlavorOption(QStringLiteral("compiler-flavor"));
247 compilerFlavorOption.setDescription(QStringLiteral("Set the compiler flavor: either \"msvc\" or \"unix\"."));
248 compilerFlavorOption.setValueName(QStringLiteral("flavor"));
249 parser.addOption(compilerFlavorOption);
250
251 QCommandLineOption noIncludeOption(QStringLiteral("i"));
252 noIncludeOption.setDescription(QStringLiteral("Do not generate an #include statement."));
253 parser.addOption(noIncludeOption);
254
255 QCommandLineOption pathPrefixOption(QStringLiteral("p"));
256 pathPrefixOption.setDescription(QStringLiteral("Path prefix for included file."));
257 pathPrefixOption.setValueName(QStringLiteral("path"));
258 pathPrefixOption.setFlags(QCommandLineOption::ShortOptionStyle);
259 parser.addOption(pathPrefixOption);
260
261 QCommandLineOption forceIncludeOption(QStringLiteral("f"));
262 forceIncludeOption.setDescription(QStringLiteral("Force #include <file> (overwrite default)."));
263 forceIncludeOption.setValueName(QStringLiteral("file"));
264 forceIncludeOption.setFlags(QCommandLineOption::ShortOptionStyle);
265 parser.addOption(forceIncludeOption);
266
267 QCommandLineOption prependIncludeOption(QStringLiteral("b"));
268 prependIncludeOption.setDescription(QStringLiteral("Prepend #include <file> (preserve default include)."));
269 prependIncludeOption.setValueName(QStringLiteral("file"));
270 prependIncludeOption.setFlags(QCommandLineOption::ShortOptionStyle);
271 parser.addOption(prependIncludeOption);
272
273 QCommandLineOption includeOption(QStringLiteral("include"));
274 includeOption.setDescription(QStringLiteral("Parse <file> as an #include before the main source(s)."));
275 includeOption.setValueName(QStringLiteral("file"));
276 parser.addOption(includeOption);
277
278 QCommandLineOption noNotesWarningsCompatOption(QStringLiteral("n"));
279 noNotesWarningsCompatOption.setDescription(QStringLiteral("Do not display notes (-nn) or warnings (-nw). Compatibility option."));
280 noNotesWarningsCompatOption.setValueName(QStringLiteral("which"));
281 noNotesWarningsCompatOption.setFlags(QCommandLineOption::ShortOptionStyle);
282 parser.addOption(noNotesWarningsCompatOption);
283
284 QCommandLineOption activeQtMode(QStringLiteral("active-qt"));
285 activeQtMode.setFlags(QCommandLineOption::HiddenFromHelp);
286 parser.addOption(activeQtMode);
287
288 QCommandLineOption qmlMacroWarningIsFatal(QStringLiteral("fatal-qml-macro-warning"));
289 qmlMacroWarningIsFatal.setFlags(QCommandLineOption::HiddenFromHelp);
290 parser.addOption(qmlMacroWarningIsFatal);
291
292 QCommandLineOption noNotesOption(QStringLiteral("no-notes"));
293 noNotesOption.setDescription(QStringLiteral("Do not display notes."));
294 parser.addOption(noNotesOption);
295
296 QCommandLineOption noWarningsOption(QStringLiteral("no-warnings"));
297 noWarningsOption.setDescription(QStringLiteral("Do not display warnings (implies --no-notes)."));
298 parser.addOption(noWarningsOption);
299
300 QCommandLineOption ignoreConflictsOption(QStringLiteral("ignore-option-clashes"));
301 ignoreConflictsOption.setDescription(QStringLiteral("Ignore all options that conflict with compilers, like -pthread conflicting with moc's -p option."));
302 parser.addOption(ignoreConflictsOption);
303
304 QCommandLineOption jsonOption(QStringLiteral("output-json"));
305 jsonOption.setDescription(QStringLiteral("In addition to generating C++ code, create a machine-readable JSON file in a file that matches the output file and an extra .json extension."));
306 parser.addOption(jsonOption);
307
308 QCommandLineOption debugIncludesOption(QStringLiteral("debug-includes"));
309 debugIncludesOption.setDescription(QStringLiteral("Display debug messages of each considered include path."));
310 parser.addOption(debugIncludesOption);
311
312 QCommandLineOption collectOption(QStringLiteral("collect-json"));
313 collectOption.setDescription(QStringLiteral("Instead of processing C++ code, collect previously generated JSON output into a single file."));
314 parser.addOption(collectOption);
315
316 QCommandLineOption depFileOption(QStringLiteral("output-dep-file"));
317 depFileOption.setDescription(
318 QStringLiteral("Output a Make-style dep file for build system consumption."));
319 parser.addOption(depFileOption);
320
321 QCommandLineOption depFilePathOption(QStringLiteral("dep-file-path"));
322 depFilePathOption.setDescription(QStringLiteral("Path where to write the dep file."));
323 depFilePathOption.setValueName(QStringLiteral("file"));
324 parser.addOption(depFilePathOption);
325
326 QCommandLineOption depFileRuleNameOption(QStringLiteral("dep-file-rule-name"));
327 depFileRuleNameOption.setDescription(
328 QStringLiteral("The rule name (first line) of the dep file."));
329 depFileRuleNameOption.setValueName(QStringLiteral("rule name"));
330 parser.addOption(depFileRuleNameOption);
331
332 QCommandLineOption requireCompleTypesOption(QStringLiteral("require-complete-types"));
333 requireCompleTypesOption.setDescription(QStringLiteral("Require complete types for better performance"));
334 parser.addOption(requireCompleTypesOption);
335
336 parser.addPositionalArgument(QStringLiteral("[header-file]"),
337 QStringLiteral("Header file to read from, otherwise stdin."));
338 parser.addPositionalArgument(QStringLiteral("[@option-file]"),
339 QStringLiteral("Read additional options from option-file."));
340 parser.addPositionalArgument(QStringLiteral("[MOC generated json file]"),
341 QStringLiteral("MOC generated json output"));
342
343 bool hasOptionFiles = false;
344 const QStringList arguments = argumentsFromCommandLineAndFile(app.arguments(), hasOptionFiles);
345 if (arguments.isEmpty())
346 return 1;
347
348 parser.process(arguments);
349
350 // used by ActiveQt's dumpcpp to suppress some functions
351 moc.activeQtMode = parser.isSet(activeQtMode);
352
353 const QStringList files = parser.positionalArguments();
354 output = parser.value(outputOption);
355 if (parser.isSet(collectOption))
356 return collectJson(files, output, hasOptionFiles);
357
358 if (files.size() > 1) {
359 error(qPrintable("Too many input files specified: '"_L1 + files.join("' '"_L1) + u'\''));
360 parser.showHelp(1);
361 } else if (!files.isEmpty()) {
362 filename = files.first();
363 }
364
365 const bool ignoreConflictingOptions = parser.isSet(ignoreConflictsOption);
366 pp.preprocessOnly = parser.isSet(preprocessOption);
367 pp.setDebugIncludes(parser.isSet(debugIncludesOption));
368 if (parser.isSet(noIncludeOption)) {
369 moc.noInclude = true;
370 autoInclude = false;
371 }
372 if (parser.isSet(requireCompleTypesOption))
373 moc.requireCompleteTypes = true;
374 if (!ignoreConflictingOptions) {
375 if (parser.isSet(forceIncludeOption)) {
376 moc.noInclude = false;
377 autoInclude = false;
378 const auto forceIncludes = parser.values(forceIncludeOption);
379 for (const QString &include : forceIncludes) {
380 moc.includeFiles.append(QFile::encodeName(include));
381 defaultInclude = false;
382 }
383 }
384 const auto prependIncludes = parser.values(prependIncludeOption);
385 for (const QString &include : prependIncludes)
386 moc.includeFiles.prepend(QFile::encodeName(include));
387 if (parser.isSet(pathPrefixOption))
388 moc.includePath = QFile::encodeName(parser.value(pathPrefixOption));
389 }
390
391 const auto includePaths = parser.values(includePathOption);
392 for (const QString &path : includePaths)
393 pp.includes += Preprocessor::IncludePath(QFile::encodeName(path));
394 QString compilerFlavor = parser.value(compilerFlavorOption);
395 if (compilerFlavor.isEmpty() || compilerFlavor == "unix"_L1) {
396 // traditional Unix compilers use both CPATH and CPLUS_INCLUDE_PATH
397 // $CPATH feeds to #include <...> and #include "...", whereas
398 // CPLUS_INCLUDE_PATH is equivalent to GCC's -isystem, so we parse later
399 const auto cpath = qgetenv("CPATH").split(QDir::listSeparator().toLatin1());
400 for (const QByteArray &p : cpath)
401 pp.includes += Preprocessor::IncludePath(p);
402 const auto cplus_include_path = qgetenv("CPLUS_INCLUDE_PATH").split(QDir::listSeparator().toLatin1());
403 for (const QByteArray &p : cplus_include_path)
404 pp.includes += Preprocessor::IncludePath(p);
405 } else if (compilerFlavor == "msvc"_L1) {
406 // MSVC uses one environment variable: INCLUDE
407 const auto include = qgetenv("INCLUDE").split(QDir::listSeparator().toLatin1());
408 for (const QByteArray &p : include)
409 pp.includes += Preprocessor::IncludePath(p);
410 } else {
411 error(qPrintable("Unknown compiler flavor '"_L1 + compilerFlavor +
412 "'; valid values are: msvc, unix."_L1));
413 parser.showHelp(1);
414 }
415
416 const auto macFrameworks = parser.values(macFrameworkOption);
417 for (const QString &path : macFrameworks) {
418 // minimalistic framework support for the mac
419 Preprocessor::IncludePath p(QFile::encodeName(path));
420 p.isFrameworkPath = true;
421 pp.includes += p;
422 }
423 const auto defines = parser.values(defineOption);
424 for (const QString &arg : defines) {
425 QByteArray name = arg.toLocal8Bit();
426 QByteArray value("1");
427 const qsizetype eq = name.indexOf('=');
428 if (eq >= 0) {
429 value = name.mid(eq + 1);
430 name = name.left(eq);
431 }
432 if (name.isEmpty()) {
433 error("Missing macro name");
434 parser.showHelp(1);
435 }
436 Macro macro;
437 macro.symbols = Preprocessor::tokenize(value, 1, Preprocessor::TokenizeDefine);
438 macro.symbols.removeLast(); // remove the EOF symbol
439 pp.macros.insert(name, macro);
440 }
441 const auto undefines = parser.values(undefineOption);
442 for (const QString &arg : undefines) {
443 QByteArray macro = arg.toLocal8Bit();
444 if (macro.isEmpty()) {
445 error("Missing macro name");
446 parser.showHelp(1);
447 }
448 pp.macros.remove(macro);
449 }
450 const QStringList noNotesCompatValues = parser.values(noNotesWarningsCompatOption);
451 if (parser.isSet(noNotesOption) || noNotesCompatValues.contains("n"_L1))
452 moc.displayNotes = false;
453 if (parser.isSet(noWarningsOption) || noNotesCompatValues.contains("w"_L1))
454 moc.displayWarnings = moc.displayNotes = false;
455 if (parser.isSet(qmlMacroWarningIsFatal))
456 moc.qmlMacroWarningIsFatal = true;
457
458 if (autoInclude) {
459 qsizetype spos = filename.lastIndexOf(QDir::separator());
460 qsizetype ppos = filename.lastIndexOf(u'.');
461 // spos >= -1 && ppos > spos => ppos >= 0
462 moc.noInclude = (ppos > spos && filename.at(ppos + 1).toLower() != u'h');
463 }
464 if (defaultInclude) {
465 if (moc.includePath.isEmpty()) {
466 if (filename.size()) {
467 if (output.size())
468 moc.includeFiles.append(combinePath(filename, output));
469 else
470 moc.includeFiles.append(QFile::encodeName(filename));
471 }
472 } else {
473 moc.includeFiles.append(combinePath(filename, filename));
474 }
475 }
476
477 if (filename.isEmpty()) {
478 filename = QStringLiteral("standard input");
479 if (!in.open(stdin, QIODevice::ReadOnly)) {
480 fprintf(stderr, "moc: cannot open standard input: %s\n", qPrintable(in.errorString()));
481 return 1;
482 }
483 } else {
484 in.setFileName(filename);
485 if (!in.open(QIODevice::ReadOnly)) {
486 fprintf(stderr, "moc: cannot open %s: %s\n", qPrintable(filename), qPrintable(in.errorString()));
487 return 1;
488 }
489 moc.filename = filename.toLocal8Bit();
490 }
491
492 const auto metadata = parser.values(metadataOption);
493 for (const QString &md : metadata) {
494 qsizetype split = md.indexOf(u'=');
495 QString key = md.left(split);
496 QString value = md.mid(split + 1);
497
498 if (split == -1 || key.isEmpty() || value.isEmpty()) {
499 error("missing key or value for option '-M'");
500 } else if (key.indexOf(u'.') != -1) {
501 // Don't allow keys with '.' for now, since we might need this
502 // format later for more advanced meta data API
503 error("A key cannot contain the letter '.' for option '-M'");
504 } else {
505 QJsonArray array = moc.metaArgs.value(key);
506 array.append(value);
507 moc.metaArgs.insert(key, array);
508 }
509 }
510
511 moc.currentFilenames.push(filename.toLocal8Bit());
512 moc.includes = pp.includes;
513
514 if (Q_UNLIKELY(parser.isSet(debugIncludesOption))) {
515 fprintf(stderr, "debug-includes: include search list:\n");
516
517 for (auto &includePath : pp.includes) {
518 fprintf(stderr, "debug-includes: '%s' framework: %d \n",
519 includePath.path.constData(),
520 includePath.isFrameworkPath);
521 }
522 fprintf(stderr, "debug-includes: end of search list.\n");
523 }
524
525 // 1. preprocess
526 const auto includeFiles = parser.values(includeOption);
527 QStringList validIncludesFiles;
528 for (const QString &includeName : includeFiles) {
529 QByteArray rawName = pp.resolveInclude(QFile::encodeName(includeName), moc.filename);
530 if (rawName.isEmpty()) {
531 fprintf(stderr, "Warning: Failed to resolve include \"%s\" for moc file %s\n",
532 includeName.toLocal8Bit().constData(),
533 moc.filename.isEmpty() ? "<standard input>" : moc.filename.constData());
534 } else {
535 QFile f(QFile::decodeName(rawName));
536 if (f.open(QIODevice::ReadOnly)) {
537 moc.symbols += Symbol(0, MOC_INCLUDE_BEGIN, rawName);
538 moc.symbols += pp.preprocessed(rawName, &f);
539 moc.symbols += Symbol(0, MOC_INCLUDE_END, rawName);
540 validIncludesFiles.append(includeName);
541 } else {
542 fprintf(stderr, "Warning: Cannot open %s included by moc file %s: %s\n",
543 rawName.constData(),
544 moc.filename.isEmpty() ? "<standard input>" : moc.filename.constData(),
545 f.errorString().toLocal8Bit().constData());
546 }
547 }
548 }
549 moc.symbols += pp.preprocessed(moc.filename, &in);
550
551 if (!pp.preprocessOnly) {
552 // 2. parse
553 moc.parse();
554 }
555
556 // 3. and output meta object code
557
558 File jsonOutput;
559
560 bool outputToFile = true;
561 if (output.size()) { // output file specified
562 out = openFileForWriting(output);
563 if (!out)
564 {
565 const auto fopen_errno = errno;
566 fprintf(stderr, "moc: Cannot create %s. Error: %s\n",
567 QFile::encodeName(output).constData(),
568 strerror(fopen_errno));
569 return 1;
570 }
571
572 if (parser.isSet(jsonOption)) {
573 const QString jsonOutputFileName = output + ".json"_L1;
574 jsonOutput = openFileForWriting(jsonOutputFileName);
575 if (!jsonOutput) {
576 const auto fopen_errno = errno;
577 fprintf(stderr, "moc: Cannot create JSON output file %s. Error: %s\n",
578 QFile::encodeName(jsonOutputFileName).constData(),
579 strerror(fopen_errno));
580 }
581 }
582 } else { // use stdout
583 out.reset(stdout);
584 outputToFile = false;
585 }
586
587 if (pp.preprocessOnly) {
588 fprintf(out.get(), "%s\n", composePreprocessorOutput(moc.symbols).constData());
589 } else if (moc.classList.isEmpty()) {
590 moc.note("No relevant classes found. No output generated.");
591 if (jsonOutput) {
592 const QJsonDocument jsonDoc(QJsonObject {
593 { "outputRevision"_L1, mocOutputRevision },
594 { "inputFile"_L1, QLatin1StringView(moc.strippedFileName()) }
595 });
596 fputs(jsonDoc.toJson().constData(), jsonOutput.get());
597 }
598 } else {
599 moc.generate(out.get(), jsonOutput.get());
600 }
601
602 out.reset();
603
604 if (parser.isSet(depFileOption)) {
605 // 4. write a Make-style dependency file (can also be consumed by Ninja).
606 QString depOutputFileName;
607 QString depRuleName = output;
608
609 if (parser.isSet(depFileRuleNameOption))
610 depRuleName = parser.value(depFileRuleNameOption);
611
612 if (parser.isSet(depFilePathOption)) {
613 depOutputFileName = parser.value(depFilePathOption);
614 } else if (outputToFile) {
615 depOutputFileName = output + ".d"_L1;
616 } else {
617 fprintf(stderr, "moc: Writing to stdout, but no depfile path specified.\n");
618 }
619
620 File depFileHandle = openFileForWriting(depOutputFileName);
621 if (!depFileHandle) {
622 const auto fopen_errno = errno;
623 fprintf(stderr, "moc: Cannot create dep output file '%s'. Error: %s\n",
624 QFile::encodeName(depOutputFileName).constData(),
625 strerror(fopen_errno));
626 }
627
628 if (depFileHandle) {
629 // First line is the path to the generated file.
630 fprintf(depFileHandle.get(), "%s: ",
631 escapeAndEncodeDependencyPath(depRuleName).constData());
632
633 QByteArrayList dependencies;
634
635 // If there's an input file, it's the first dependency.
636 if (!filename.isEmpty()) {
637 dependencies.append(escapeAndEncodeDependencyPath(filename).constData());
638 }
639
640 // Additional passed-in includes are dependencies (like moc_predefs.h).
641 for (const QString &includeName : validIncludesFiles) {
642 dependencies.append(escapeAndEncodeDependencyPath(includeName).constData());
643 }
644
645 // Plugin metadata json files discovered via Q_PLUGIN_METADATA macros are also
646 // dependencies.
647 for (const QString &pluginMetadataFile : moc.parsedPluginMetadataFiles) {
648 dependencies.append(escapeAndEncodeDependencyPath(pluginMetadataFile).constData());
649 }
650
651 // All pre-processed includes are dependnecies.
652 // Sort the entries for easier human consumption.
653 auto includeList = pp.preprocessedIncludes.values();
654 std::sort(includeList.begin(), includeList.end());
655
656 for (QByteArray &includeName : includeList) {
657 dependencies.append(escapeDependencyPath(includeName));
658 }
659
660 // Join dependencies, output them, and output a final new line.
661 const auto dependenciesJoined = dependencies.join(QByteArrayLiteral(" \\\n "));
662 fprintf(depFileHandle.get(), "%s\n", dependenciesJoined.constData());
663 }
664 }
665
666 return 0;
667}
668
670
671int main(int _argc, char **_argv)
672{
673 return QT_PREPEND_NAMESPACE(runMoc)(_argc, _argv);
674}
Definition moc.h:223
void parse()
Definition moc.cpp:677
bool requireCompleteTypes
Definition moc.h:235
void generate(FILE *out, FILE *jsonOutput)
Definition moc.cpp:1190
bool noInclude
Definition moc.h:233
static bool preprocessOnly
The QCommandLineOption class defines a possible command-line option. \inmodule QtCore.
The QCommandLineParser class provides a means for handling the command line options.
#define qPrintable(string)
Definition qstring.h:1685
#define QStringLiteral(str)
Definition qstring.h:1826
static QByteArray combinePath(const QString &infile, const QString &outfile)
Definition main.cpp:40
static bool hasNext(const Symbols &symbols, int i)
Definition main.cpp:78
static const Symbol & next(const Symbols &symbols, int &i)
Definition main.cpp:81
int runMoc(int argc, char **argv)
Definition main.cpp:170
static auto openFileForWriting(const QString &name)
Definition main.cpp:62
void error(const char *msg="Invalid argument")
Definition main.cpp:56
static QStringList argumentsFromCommandLineAndFile(const QStringList &arguments, bool &hasOptionFiles)
Definition main.cpp:138
QByteArray composePreprocessorOutput(const Symbols &symbols)
Definition main.cpp:85
int main(int argc, char *argv[])
[ctor_close]
Simple structure used by the Doc and DocParser classes.
bool isVariadic
bool isFunction
Token token
Definition symbols.h:58
int lineNum
Definition symbols.h:57
QList< Symbol > Symbols
Definition symbols.h:75