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
48 QT_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
combinePath(const QString & infile,const QString & outfile)61 static 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
error(const char * msg="Invalid argument")77 void error(const char *msg = "Invalid argument")
78 {
79 if (msg)
80 fprintf(stderr, "moc: %s\n", msg);
81 }
82
83 struct ScopedPointerFileCloser
84 {
cleanupScopedPointerFileCloser85 static inline void cleanup(FILE *handle) { if (handle) fclose(handle); }
86 };
87
hasNext(const Symbols & symbols,int i)88 static inline bool hasNext(const Symbols &symbols, int i)
89 { return (i < symbols.size()); }
90
next(const Symbols & symbols,int & i)91 static inline const Symbol &next(const Symbols &symbols, int &i)
92 { return symbols.at(i++); }
93
94
composePreprocessorOutput(const Symbols & symbols)95 QByteArray 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
argumentsFromCommandLineAndFile(const QStringList & arguments)148 static 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.
189 template <typename T> struct CharType;
190 template <> struct CharType<QString> { using type = QLatin1Char; };
191 template <> struct CharType<QByteArray> { using type = char; };
192 template <typename StringType>
escapeDependencyPath(const StringType & path)193 StringType 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
escapeAndEncodeDependencyPath(const QString & path)217 QByteArray escapeAndEncodeDependencyPath(const QString &path)
218 {
219 return QFile::encodeName(escapeDependencyPath(path));
220 }
221
runMoc(int argc,char ** argv)222 int 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 = 0;
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 parser.addPositionalArgument(QStringLiteral("[header-file]"),
371 QStringLiteral("Header file to read from, otherwise stdin."));
372 parser.addPositionalArgument(QStringLiteral("[@option-file]"),
373 QStringLiteral("Read additional options from option-file."));
374 parser.addPositionalArgument(QStringLiteral("[MOC generated json file]"),
375 QStringLiteral("MOC generated json output"));
376
377 const QStringList arguments = argumentsFromCommandLineAndFile(app.arguments());
378 if (arguments.isEmpty())
379 return 1;
380
381 parser.process(arguments);
382
383 const QStringList files = parser.positionalArguments();
384 output = parser.value(outputOption);
385 if (parser.isSet(collectOption))
386 return collectJson(files, output);
387
388 if (files.count() > 1) {
389 error(qPrintable(QLatin1String("Too many input files specified: '") + files.join(QLatin1String("' '")) + QLatin1Char('\'')));
390 parser.showHelp(1);
391 } else if (!files.isEmpty()) {
392 filename = files.first();
393 }
394
395 const bool ignoreConflictingOptions = parser.isSet(ignoreConflictsOption);
396 pp.preprocessOnly = parser.isSet(preprocessOption);
397 if (parser.isSet(noIncludeOption)) {
398 moc.noInclude = true;
399 autoInclude = false;
400 }
401 if (!ignoreConflictingOptions) {
402 if (parser.isSet(forceIncludeOption)) {
403 moc.noInclude = false;
404 autoInclude = false;
405 const auto forceIncludes = parser.values(forceIncludeOption);
406 for (const QString &include : forceIncludes) {
407 moc.includeFiles.append(QFile::encodeName(include));
408 defaultInclude = false;
409 }
410 }
411 const auto prependIncludes = parser.values(prependIncludeOption);
412 for (const QString &include : prependIncludes)
413 moc.includeFiles.prepend(QFile::encodeName(include));
414 if (parser.isSet(pathPrefixOption))
415 moc.includePath = QFile::encodeName(parser.value(pathPrefixOption));
416 }
417
418 const auto includePaths = parser.values(includePathOption);
419 for (const QString &path : includePaths)
420 pp.includes += Preprocessor::IncludePath(QFile::encodeName(path));
421 QString compilerFlavor = parser.value(compilerFlavorOption);
422 if (compilerFlavor.isEmpty() || compilerFlavor == QLatin1String("unix")) {
423 // traditional Unix compilers use both CPATH and CPLUS_INCLUDE_PATH
424 // $CPATH feeds to #include <...> and #include "...", whereas
425 // CPLUS_INCLUDE_PATH is equivalent to GCC's -isystem, so we parse later
426 const auto cpath = qgetenv("CPATH").split(QDir::listSeparator().toLatin1());
427 for (const QByteArray &p : cpath)
428 pp.includes += Preprocessor::IncludePath(p);
429 const auto cplus_include_path = qgetenv("CPLUS_INCLUDE_PATH").split(QDir::listSeparator().toLatin1());
430 for (const QByteArray &p : cplus_include_path)
431 pp.includes += Preprocessor::IncludePath(p);
432 } else if (compilerFlavor == QLatin1String("msvc")) {
433 // MSVC uses one environment variable: INCLUDE
434 const auto include = qgetenv("INCLUDE").split(QDir::listSeparator().toLatin1());
435 for (const QByteArray &p : include)
436 pp.includes += Preprocessor::IncludePath(p);
437 } else {
438 error(qPrintable(QLatin1String("Unknown compiler flavor '") + compilerFlavor +
439 QLatin1String("'; valid values are: msvc, unix.")));
440 parser.showHelp(1);
441 }
442
443 const auto macFrameworks = parser.values(macFrameworkOption);
444 for (const QString &path : macFrameworks) {
445 // minimalistic framework support for the mac
446 Preprocessor::IncludePath p(QFile::encodeName(path));
447 p.isFrameworkPath = true;
448 pp.includes += p;
449 }
450 const auto defines = parser.values(defineOption);
451 for (const QString &arg : defines) {
452 QByteArray name = arg.toLocal8Bit();
453 QByteArray value("1");
454 int eq = name.indexOf('=');
455 if (eq >= 0) {
456 value = name.mid(eq + 1);
457 name = name.left(eq);
458 }
459 if (name.isEmpty()) {
460 error("Missing macro name");
461 parser.showHelp(1);
462 }
463 Macro macro;
464 macro.symbols = Preprocessor::tokenize(value, 1, Preprocessor::TokenizeDefine);
465 macro.symbols.removeLast(); // remove the EOF symbol
466 pp.macros.insert(name, macro);
467 }
468 const auto undefines = parser.values(undefineOption);
469 for (const QString &arg : undefines) {
470 QByteArray macro = arg.toLocal8Bit();
471 if (macro.isEmpty()) {
472 error("Missing macro name");
473 parser.showHelp(1);
474 }
475 pp.macros.remove(macro);
476 }
477 const QStringList noNotesCompatValues = parser.values(noNotesWarningsCompatOption);
478 if (parser.isSet(noNotesOption) || noNotesCompatValues.contains(QLatin1String("n")))
479 moc.displayNotes = false;
480 if (parser.isSet(noWarningsOption) || noNotesCompatValues.contains(QLatin1String("w")))
481 moc.displayWarnings = moc.displayNotes = false;
482
483 if (autoInclude) {
484 int spos = filename.lastIndexOf(QDir::separator());
485 int ppos = filename.lastIndexOf(QLatin1Char('.'));
486 // spos >= -1 && ppos > spos => ppos >= 0
487 moc.noInclude = (ppos > spos && filename.at(ppos + 1).toLower() != QLatin1Char('h'));
488 }
489 if (defaultInclude) {
490 if (moc.includePath.isEmpty()) {
491 if (filename.size()) {
492 if (output.size())
493 moc.includeFiles.append(combinePath(filename, output));
494 else
495 moc.includeFiles.append(QFile::encodeName(filename));
496 }
497 } else {
498 moc.includeFiles.append(combinePath(filename, filename));
499 }
500 }
501
502 if (filename.isEmpty()) {
503 filename = QStringLiteral("standard input");
504 in.open(stdin, QIODevice::ReadOnly);
505 } else {
506 in.setFileName(filename);
507 if (!in.open(QIODevice::ReadOnly)) {
508 fprintf(stderr, "moc: %s: No such file\n", qPrintable(filename));
509 return 1;
510 }
511 moc.filename = filename.toLocal8Bit();
512 }
513
514 const auto metadata = parser.values(metadataOption);
515 for (const QString &md : metadata) {
516 int split = md.indexOf(QLatin1Char('='));
517 QString key = md.left(split);
518 QString value = md.mid(split + 1);
519
520 if (split == -1 || key.isEmpty() || value.isEmpty()) {
521 error("missing key or value for option '-M'");
522 } else if (key.indexOf(QLatin1Char('.')) != -1) {
523 // Don't allow keys with '.' for now, since we might need this
524 // format later for more advanced meta data API
525 error("A key cannot contain the letter '.' for option '-M'");
526 } else {
527 QJsonArray array = moc.metaArgs.value(key);
528 array.append(value);
529 moc.metaArgs.insert(key, array);
530 }
531 }
532
533 moc.currentFilenames.push(filename.toLocal8Bit());
534 moc.includes = pp.includes;
535
536 // 1. preprocess
537 const auto includeFiles = parser.values(includeOption);
538 QStringList validIncludesFiles;
539 for (const QString &includeName : includeFiles) {
540 QByteArray rawName = pp.resolveInclude(QFile::encodeName(includeName), moc.filename);
541 if (rawName.isEmpty()) {
542 fprintf(stderr, "Warning: Failed to resolve include \"%s\" for moc file %s\n",
543 includeName.toLocal8Bit().constData(),
544 moc.filename.isEmpty() ? "<standard input>" : moc.filename.constData());
545 } else {
546 QFile f(QFile::decodeName(rawName));
547 if (f.open(QIODevice::ReadOnly)) {
548 moc.symbols += Symbol(0, MOC_INCLUDE_BEGIN, rawName);
549 moc.symbols += pp.preprocessed(rawName, &f);
550 moc.symbols += Symbol(0, MOC_INCLUDE_END, rawName);
551 validIncludesFiles.append(includeName);
552 } else {
553 fprintf(stderr, "Warning: Cannot open %s included by moc file %s: %s\n",
554 rawName.constData(),
555 moc.filename.isEmpty() ? "<standard input>" : moc.filename.constData(),
556 f.errorString().toLocal8Bit().constData());
557 }
558 }
559 }
560 moc.symbols += pp.preprocessed(moc.filename, &in);
561
562 if (!pp.preprocessOnly) {
563 // 2. parse
564 moc.parse();
565 }
566
567 // 3. and output meta object code
568
569 QScopedPointer<FILE, ScopedPointerFileCloser> jsonOutput;
570
571 bool outputToFile = true;
572 if (output.size()) { // output file specified
573 #if defined(_MSC_VER)
574 if (_wfopen_s(&out, reinterpret_cast<const wchar_t *>(output.utf16()), L"w") != 0)
575 #else
576 out = fopen(QFile::encodeName(output).constData(), "w"); // create output file
577 if (!out)
578 #endif
579 {
580 fprintf(stderr, "moc: Cannot create %s\n", QFile::encodeName(output).constData());
581 return 1;
582 }
583
584 if (parser.isSet(jsonOption)) {
585 const QString jsonOutputFileName = output + QLatin1String(".json");
586 FILE *f;
587 #if defined(_MSC_VER)
588 if (_wfopen_s(&f, reinterpret_cast<const wchar_t *>(jsonOutputFileName.utf16()), L"w") != 0)
589 #else
590 f = fopen(QFile::encodeName(jsonOutputFileName).constData(), "w");
591 if (!f)
592 #endif
593 fprintf(stderr, "moc: Cannot create JSON output file %s. %s\n",
594 QFile::encodeName(jsonOutputFileName).constData(),
595 strerror(errno));
596 jsonOutput.reset(f);
597 }
598 } else { // use stdout
599 out = stdout;
600 outputToFile = false;
601 }
602
603 if (pp.preprocessOnly) {
604 fprintf(out, "%s\n", composePreprocessorOutput(moc.symbols).constData());
605 } else {
606 if (moc.classList.isEmpty())
607 moc.note("No relevant classes found. No output generated.");
608 else
609 moc.generate(out, jsonOutput.data());
610 }
611
612 if (output.size())
613 fclose(out);
614
615 if (parser.isSet(depFileOption)) {
616 // 4. write a Make-style dependency file (can also be consumed by Ninja).
617 QString depOutputFileName;
618 QString depRuleName = output;
619
620 if (parser.isSet(depFileRuleNameOption))
621 depRuleName = parser.value(depFileRuleNameOption);
622
623 if (parser.isSet(depFilePathOption)) {
624 depOutputFileName = parser.value(depFilePathOption);
625 } else if (outputToFile) {
626 depOutputFileName = output + QLatin1String(".d");
627 } else {
628 fprintf(stderr, "moc: Writing to stdout, but no depfile path specified.\n");
629 }
630
631 QScopedPointer<FILE, ScopedPointerFileCloser> depFileHandle;
632 FILE *depFileHandleRaw;
633 #if defined(_MSC_VER)
634 if (_wfopen_s(&depFileHandleRaw,
635 reinterpret_cast<const wchar_t *>(depOutputFileName.utf16()), L"w") != 0)
636 #else
637 depFileHandleRaw = fopen(QFile::encodeName(depOutputFileName).constData(), "w");
638 if (!depFileHandleRaw)
639 #endif
640 fprintf(stderr, "moc: Cannot create dep output file '%s'. %s\n",
641 QFile::encodeName(depOutputFileName).constData(),
642 strerror(errno));
643 depFileHandle.reset(depFileHandleRaw);
644
645 if (!depFileHandle.isNull()) {
646 // First line is the path to the generated file.
647 fprintf(depFileHandle.data(), "%s: ",
648 escapeAndEncodeDependencyPath(depRuleName).constData());
649
650 QByteArrayList dependencies;
651
652 // If there's an input file, it's the first dependency.
653 if (!filename.isEmpty()) {
654 dependencies.append(escapeAndEncodeDependencyPath(filename).constData());
655 }
656
657 // Additional passed-in includes are dependencies (like moc_predefs.h).
658 for (const QString &includeName : validIncludesFiles) {
659 dependencies.append(escapeAndEncodeDependencyPath(includeName).constData());
660 }
661
662 // Plugin metadata json files discovered via Q_PLUGIN_METADATA macros are also
663 // dependencies.
664 for (const QString &pluginMetadataFile : moc.parsedPluginMetadataFiles) {
665 dependencies.append(escapeAndEncodeDependencyPath(pluginMetadataFile).constData());
666 }
667
668 // All pre-processed includes are dependnecies.
669 // Sort the entries for easier human consumption.
670 auto includeList = pp.preprocessedIncludes.values();
671 std::sort(includeList.begin(), includeList.end());
672
673 for (QByteArray &includeName : includeList) {
674 dependencies.append(escapeDependencyPath(includeName));
675 }
676
677 // Join dependencies, output them, and output a final new line.
678 const auto dependenciesJoined = dependencies.join(QByteArrayLiteral(" \\\n "));
679 fprintf(depFileHandle.data(), "%s\n", dependenciesJoined.constData());
680 }
681 }
682
683 return 0;
684 }
685
686 QT_END_NAMESPACE
687
main(int _argc,char ** _argv)688 int main(int _argc, char **_argv)
689 {
690 return QT_PREPEND_NAMESPACE(runMoc)(_argc, _argv);
691 }
692