172 QCoreApplication app(argc, argv);
173 QCoreApplication::setApplicationVersion(QString::fromLatin1(QT_VERSION_STR));
175 QCoreApplication::setApplicationName(QString::fromLatin1(
"moc"));
177 bool autoInclude =
true;
178 bool defaultInclude =
true;
185 Macro dummyVariadicFunctionMacro;
188 dummyVariadicFunctionMacro.arguments += Symbol(0, PP_IDENTIFIER,
"__VA_ARGS__");
189 pp
.macros[
"__attribute__"] = dummyVariadicFunctionMacro;
190 pp
.macros[
"__declspec"] = dummyVariadicFunctionMacro;
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);
207 outputOption.setDescription(
QStringLiteral(
"Write output to file rather than stdout."));
209 outputOption.setFlags(QCommandLineOption::ShortOptionStyle);
210 parser.addOption(outputOption);
213 includePathOption.setDescription(
QStringLiteral(
"Add dir to the include path for header files."));
215 includePathOption.setFlags(QCommandLineOption::ShortOptionStyle);
216 parser.addOption(includePathOption);
219 macFrameworkOption.setDescription(
QStringLiteral(
"Add Mac framework to the include path for header files."));
221 macFrameworkOption.setFlags(QCommandLineOption::ShortOptionStyle);
222 parser.addOption(macFrameworkOption);
225 preprocessOption.setDescription(
QStringLiteral(
"Preprocess only; do not generate meta object code."));
226 parser.addOption(preprocessOption);
229 defineOption.setDescription(
QStringLiteral(
"Define macro, with optional definition."));
231 defineOption.setFlags(QCommandLineOption::ShortOptionStyle);
232 parser.addOption(defineOption);
237 undefineOption.setFlags(QCommandLineOption::ShortOptionStyle);
238 parser.addOption(undefineOption);
241 metadataOption.setDescription(
QStringLiteral(
"Add key/value pair to plugin meta data"));
243 metadataOption.setFlags(QCommandLineOption::ShortOptionStyle);
244 parser.addOption(metadataOption);
247 compilerFlavorOption.setDescription(
QStringLiteral(
"Set the compiler flavor: either \"msvc\" or \"unix\"."));
249 parser.addOption(compilerFlavorOption);
252 noIncludeOption.setDescription(
QStringLiteral(
"Do not generate an #include statement."));
253 parser.addOption(noIncludeOption);
256 pathPrefixOption.setDescription(
QStringLiteral(
"Path prefix for included file."));
258 pathPrefixOption.setFlags(QCommandLineOption::ShortOptionStyle);
259 parser.addOption(pathPrefixOption);
262 forceIncludeOption.setDescription(
QStringLiteral(
"Force #include <file> (overwrite default)."));
264 forceIncludeOption.setFlags(QCommandLineOption::ShortOptionStyle);
265 parser.addOption(forceIncludeOption);
268 prependIncludeOption.setDescription(
QStringLiteral(
"Prepend #include <file> (preserve default include)."));
270 prependIncludeOption.setFlags(QCommandLineOption::ShortOptionStyle);
271 parser.addOption(prependIncludeOption);
274 includeOption.setDescription(
QStringLiteral(
"Parse <file> as an #include before the main source(s)."));
276 parser.addOption(includeOption);
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);
285 activeQtMode.setFlags(QCommandLineOption::HiddenFromHelp);
286 parser.addOption(activeQtMode);
289 qmlMacroWarningIsFatal.setFlags(QCommandLineOption::HiddenFromHelp);
290 parser.addOption(qmlMacroWarningIsFatal);
293 noNotesOption.setDescription(
QStringLiteral(
"Do not display notes."));
294 parser.addOption(noNotesOption);
297 noWarningsOption.setDescription(
QStringLiteral(
"Do not display warnings (implies --no-notes)."));
298 parser.addOption(noWarningsOption);
301 ignoreConflictsOption.setDescription(
QStringLiteral(
"Ignore all options that conflict with compilers, like -pthread conflicting with moc's -p option."));
302 parser.addOption(ignoreConflictsOption);
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);
309 debugIncludesOption.setDescription(
QStringLiteral(
"Display debug messages of each considered include path."));
310 parser.addOption(debugIncludesOption);
313 collectOption.setDescription(
QStringLiteral(
"Instead of processing C++ code, collect previously generated JSON output into a single file."));
314 parser.addOption(collectOption);
317 depFileOption.setDescription(
318 QStringLiteral(
"Output a Make-style dep file for build system consumption."));
319 parser.addOption(depFileOption);
322 depFilePathOption.setDescription(
QStringLiteral(
"Path where to write the dep file."));
324 parser.addOption(depFilePathOption);
327 depFileRuleNameOption.setDescription(
330 parser.addOption(depFileRuleNameOption);
333 requireCompleTypesOption.setDescription(
QStringLiteral(
"Require complete types for better performance"));
334 parser.addOption(requireCompleTypesOption);
340 parser.addPositionalArgument(
QStringLiteral(
"[MOC generated json file]"),
343 bool hasOptionFiles =
false;
344 const QStringList arguments = argumentsFromCommandLineAndFile(app.arguments(), hasOptionFiles);
345 if (arguments.isEmpty())
348 parser.process(arguments);
351 moc.activeQtMode = parser.isSet(activeQtMode);
353 const QStringList files = parser.positionalArguments();
354 output = parser.value(outputOption);
355 if (parser.isSet(collectOption))
356 return collectJson(files, output, hasOptionFiles);
358 if (files.size() > 1) {
359 error(
qPrintable(
"Too many input files specified: '"_L1 + files.join(
"' '"_L1) + u'\''));
361 }
else if (!files.isEmpty()) {
362 filename = files.first();
365 const bool ignoreConflictingOptions = parser.isSet(ignoreConflictsOption);
366 pp.preprocessOnly = parser.isSet(preprocessOption);
367 pp.setDebugIncludes(parser.isSet(debugIncludesOption));
368 if (parser.isSet(noIncludeOption)) {
372 if (parser.isSet(requireCompleTypesOption))
374 if (!ignoreConflictingOptions) {
375 if (parser.isSet(forceIncludeOption)) {
378 const auto forceIncludes = parser.values(forceIncludeOption);
379 for (
const QString &include : forceIncludes) {
380 moc.includeFiles.append(QFile::encodeName(include));
381 defaultInclude =
false;
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));
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) {
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) {
407 const auto include = qgetenv(
"INCLUDE").split(QDir::listSeparator().toLatin1());
408 for (
const QByteArray &p : include)
409 pp.includes += Preprocessor::IncludePath(p);
411 error(
qPrintable(
"Unknown compiler flavor '"_L1 + compilerFlavor +
412 "'; valid values are: msvc, unix."_L1));
416 const auto macFrameworks = parser.values(macFrameworkOption);
417 for (
const QString &path : macFrameworks) {
419 Preprocessor::IncludePath p(QFile::encodeName(path));
420 p.isFrameworkPath =
true;
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(
'=');
429 value = name.mid(eq + 1);
430 name = name.left(eq);
432 if (name.isEmpty()) {
433 error(
"Missing macro name");
437 macro.symbols = Preprocessor::tokenize(value, 1, Preprocessor::TokenizeDefine);
438 macro.symbols.removeLast();
439 pp.macros.insert(name, macro);
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");
448 pp.macros.remove(macro);
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;
459 qsizetype spos = filename.lastIndexOf(QDir::separator());
460 qsizetype ppos = filename.lastIndexOf(u'.');
462 moc
.noInclude = (ppos > spos && filename.at(ppos + 1).toLower() != u'h');
464 if (defaultInclude) {
465 if (moc.includePath.isEmpty()) {
466 if (filename.size()) {
468 moc.includeFiles.append(combinePath(filename, output));
470 moc.includeFiles.append(QFile::encodeName(filename));
473 moc.includeFiles.append(combinePath(filename, filename));
477 if (filename.isEmpty()) {
479 if (!in.open(stdin, QIODevice::ReadOnly)) {
480 fprintf(stderr,
"moc: cannot open standard input: %s\n",
qPrintable(in.errorString()));
484 in.setFileName(filename);
485 if (!in.open(QIODevice::ReadOnly)) {
486 fprintf(stderr,
"moc: cannot open %s: %s\n",
qPrintable(filename),
qPrintable(in.errorString()));
489 moc.filename = filename.toLocal8Bit();
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);
498 if (split == -1 || key.isEmpty() || value.isEmpty()) {
499 error(
"missing key or value for option '-M'");
500 }
else if (key.indexOf(u'.') != -1) {
503 error(
"A key cannot contain the letter '.' for option '-M'");
505 QJsonArray array = moc.metaArgs.value(key);
507 moc.metaArgs.insert(key, array);
511 moc.currentFilenames.push(filename.toLocal8Bit());
512 moc.includes = pp.includes;
514 if (Q_UNLIKELY(parser.isSet(debugIncludesOption))) {
515 fprintf(stderr,
"debug-includes: include search list:\n");
517 for (
auto &includePath : pp.includes) {
518 fprintf(stderr,
"debug-includes: '%s' framework: %d \n",
519 includePath.path.constData(),
520 includePath.isFrameworkPath);
522 fprintf(stderr,
"debug-includes: end of search list.\n");
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());
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);
542 fprintf(stderr,
"Warning: Cannot open %s included by moc file %s: %s\n",
544 moc.filename.isEmpty() ?
"<standard input>" : moc.filename.constData(),
545 f.errorString().toLocal8Bit().constData());
549 moc.symbols += pp.preprocessed(moc.filename, &in);
560 bool outputToFile =
true;
562 out = openFileForWriting(output);
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));
572 if (parser.isSet(jsonOption)) {
573 const QString jsonOutputFileName = output +
".json"_L1;
574 jsonOutput = openFileForWriting(jsonOutputFileName);
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));
584 outputToFile =
false;
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.");
592 const QJsonDocument jsonDoc(QJsonObject {
593 {
"outputRevision"_L1, mocOutputRevision },
594 {
"inputFile"_L1, QLatin1StringView(moc.strippedFileName()) }
596 fputs(jsonDoc.toJson().constData(), jsonOutput.get());
604 if (parser.isSet(depFileOption)) {
606 QString depOutputFileName;
607 QString depRuleName = output;
609 if (parser.isSet(depFileRuleNameOption))
610 depRuleName = parser.value(depFileRuleNameOption);
612 if (parser.isSet(depFilePathOption)) {
613 depOutputFileName = parser.value(depFilePathOption);
614 }
else if (outputToFile) {
615 depOutputFileName = output +
".d"_L1;
617 fprintf(stderr,
"moc: Writing to stdout, but no depfile path specified.\n");
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));
630 fprintf(depFileHandle.get(),
"%s: ",
631 escapeAndEncodeDependencyPath(depRuleName).constData());
633 QByteArrayList dependencies;
636 if (!filename.isEmpty()) {
637 dependencies.append(escapeAndEncodeDependencyPath(filename).constData());
641 for (
const QString &includeName : validIncludesFiles) {
642 dependencies.append(escapeAndEncodeDependencyPath(includeName).constData());
647 for (
const QString &pluginMetadataFile : moc.parsedPluginMetadataFiles) {
648 dependencies.append(escapeAndEncodeDependencyPath(pluginMetadataFile).constData());
653 auto includeList = pp.preprocessedIncludes.values();
654 std::sort(includeList.begin(), includeList.end());
656 for (QByteArray &includeName : includeList) {
657 dependencies.append(escapeDependencyPath(includeName));
661 const auto dependenciesJoined = dependencies.join(QByteArrayLiteral(
" \\\n "));
662 fprintf(depFileHandle.get(),
"%s\n", dependenciesJoined.constData());