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