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);
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#if defined(Q_OS_APPLE)
394 if (path.endsWith(
".framework"_L1)) {
398 auto frameworkPath = path.sliced(0, path.lastIndexOf(QDir::separator()));
399 Preprocessor::IncludePath p(QFile::encodeName(frameworkPath));
400 p.isFrameworkPath =
true;
405 pp.includes += Preprocessor::IncludePath(QFile::encodeName(path));
408 QString compilerFlavor = parser.value(compilerFlavorOption);
409 if (compilerFlavor.isEmpty() || compilerFlavor ==
"unix"_L1) {
413 const auto cpath = qgetenv(
"CPATH").split(QDir::listSeparator().toLatin1());
414 for (
const QByteArray &p : cpath)
415 pp.includes += Preprocessor::IncludePath(p);
416 const auto cplus_include_path = qgetenv(
"CPLUS_INCLUDE_PATH").split(QDir::listSeparator().toLatin1());
417 for (
const QByteArray &p : cplus_include_path)
418 pp.includes += Preprocessor::IncludePath(p);
419 }
else if (compilerFlavor ==
"msvc"_L1) {
421 const auto include = qgetenv(
"INCLUDE").split(QDir::listSeparator().toLatin1());
422 for (
const QByteArray &p : include)
423 pp.includes += Preprocessor::IncludePath(p);
425 error(
qPrintable(
"Unknown compiler flavor '"_L1 + compilerFlavor +
426 "'; valid values are: msvc, unix."_L1));
430 const auto macFrameworks = parser.values(macFrameworkOption);
431 for (
const QString &path : macFrameworks) {
433 Preprocessor::IncludePath p(QFile::encodeName(path));
434 p.isFrameworkPath =
true;
437 const auto defines = parser.values(defineOption);
438 for (
const QString &arg : defines) {
439 QByteArray name = arg.toLocal8Bit();
440 QByteArray value(
"1");
441 const qsizetype eq = name.indexOf(
'=');
443 value = name.mid(eq + 1);
444 name = name.left(eq);
446 if (name.isEmpty()) {
447 error(
"Missing macro name");
451 macro.symbols = Preprocessor::tokenize(value, 1, Preprocessor::TokenizeDefine);
452 macro.symbols.removeLast();
453 pp.macros.insert(name, macro);
455 const auto undefines = parser.values(undefineOption);
456 for (
const QString &arg : undefines) {
457 QByteArray macro = arg.toLocal8Bit();
458 if (macro.isEmpty()) {
459 error(
"Missing macro name");
462 pp.macros.remove(macro);
464 const QStringList noNotesCompatValues = parser.values(noNotesWarningsCompatOption);
465 if (parser.isSet(noNotesOption) || noNotesCompatValues.contains(
"n"_L1))
466 moc.displayNotes =
false;
467 if (parser.isSet(noWarningsOption) || noNotesCompatValues.contains(
"w"_L1))
468 moc.displayWarnings = moc.displayNotes =
false;
469 if (parser.isSet(qmlMacroWarningIsFatal))
470 moc.qmlMacroWarningIsFatal =
true;
473 qsizetype spos = filename.lastIndexOf(QDir::separator());
474 qsizetype ppos = filename.lastIndexOf(u'.');
476 moc
.noInclude = (ppos > spos && filename.at(ppos + 1).toLower() != u'h');
478 if (defaultInclude) {
479 if (moc.includePath.isEmpty()) {
480 if (filename.size()) {
482 moc.includeFiles.append(combinePath(filename, output));
484 moc.includeFiles.append(QFile::encodeName(filename));
487 moc.includeFiles.append(combinePath(filename, filename));
491 if (filename.isEmpty()) {
493 if (!in.open(stdin, QIODevice::ReadOnly)) {
494 fprintf(stderr,
"moc: cannot open standard input: %s\n",
qPrintable(in.errorString()));
498 in.setFileName(filename);
499 if (!in.open(QIODevice::ReadOnly)) {
500 fprintf(stderr,
"moc: cannot open %s: %s\n",
qPrintable(filename),
qPrintable(in.errorString()));
503 moc.filename = filename.toLocal8Bit();
506 const auto metadata = parser.values(metadataOption);
507 for (
const QString &md : metadata) {
508 qsizetype split = md.indexOf(u'=');
509 QString key = md.left(split);
510 QString value = md.mid(split + 1);
512 if (split == -1 || key.isEmpty() || value.isEmpty()) {
513 error(
"missing key or value for option '-M'");
514 }
else if (key.indexOf(u'.') != -1) {
517 error(
"A key cannot contain the letter '.' for option '-M'");
519 QJsonArray array = moc.metaArgs.value(key);
521 moc.metaArgs.insert(key, array);
525 moc.currentFilenames.push(filename.toLocal8Bit());
526 moc.includes = pp.includes;
528 if (Q_UNLIKELY(parser.isSet(debugIncludesOption))) {
529 fprintf(stderr,
"debug-includes: include search list:\n");
531 for (
auto &includePath : pp.includes) {
532 fprintf(stderr,
"debug-includes: '%s' framework: %d \n",
533 includePath.path.constData(),
534 includePath.isFrameworkPath);
536 fprintf(stderr,
"debug-includes: end of search list.\n");
540 const auto includeFiles = parser.values(includeOption);
541 QStringList validIncludesFiles;
542 for (
const QString &includeName : includeFiles) {
543 QByteArray rawName = pp.resolveInclude(QFile::encodeName(includeName), moc.filename);
544 if (rawName.isEmpty()) {
545 fprintf(stderr,
"Warning: Failed to resolve include \"%s\" for moc file %s\n",
546 includeName.toLocal8Bit().constData(),
547 moc.filename.isEmpty() ?
"<standard input>" : moc.filename.constData());
549 QFile f(QFile::decodeName(rawName));
550 if (f.open(QIODevice::ReadOnly)) {
551 moc.symbols += Symbol(0, MOC_INCLUDE_BEGIN, rawName);
552 moc.symbols += pp.preprocessed(rawName, &f);
553 moc.symbols += Symbol(0, MOC_INCLUDE_END, rawName);
554 validIncludesFiles.append(includeName);
556 fprintf(stderr,
"Warning: Cannot open %s included by moc file %s: %s\n",
558 moc.filename.isEmpty() ?
"<standard input>" : moc.filename.constData(),
559 f.errorString().toLocal8Bit().constData());
563 moc.symbols += pp.preprocessed(moc.filename, &in);
574 bool outputToFile =
true;
576 out = openFileForWriting(output);
579 const auto fopen_errno = errno;
580 fprintf(stderr,
"moc: Cannot create %s. Error: %s\n",
581 QFile::encodeName(output).constData(),
582 strerror(fopen_errno));
586 if (parser.isSet(jsonOption)) {
587 const QString jsonOutputFileName = output +
".json"_L1;
588 jsonOutput = openFileForWriting(jsonOutputFileName);
590 const auto fopen_errno = errno;
591 fprintf(stderr,
"moc: Cannot create JSON output file %s. Error: %s\n",
592 QFile::encodeName(jsonOutputFileName).constData(),
593 strerror(fopen_errno));
598 outputToFile =
false;
602 fprintf(out.get(),
"%s\n", composePreprocessorOutput(moc.symbols).constData());
603 }
else if (moc.classList.isEmpty()) {
604 moc.note(
"No relevant classes found. No output generated.");
606 const QJsonDocument jsonDoc(QJsonObject {
607 {
"outputRevision"_L1, mocOutputRevision },
608 {
"inputFile"_L1, QLatin1StringView(moc.strippedFileName()) }
610 fputs(jsonDoc.toJson().constData(), jsonOutput.get());
618 if (parser.isSet(depFileOption)) {
620 QString depOutputFileName;
621 QString depRuleName = output;
623 if (parser.isSet(depFileRuleNameOption))
624 depRuleName = parser.value(depFileRuleNameOption);
626 if (parser.isSet(depFilePathOption)) {
627 depOutputFileName = parser.value(depFilePathOption);
628 }
else if (outputToFile) {
629 depOutputFileName = output +
".d"_L1;
631 fprintf(stderr,
"moc: Writing to stdout, but no depfile path specified.\n");
634 File depFileHandle = openFileForWriting(depOutputFileName);
635 if (!depFileHandle) {
636 const auto fopen_errno = errno;
637 fprintf(stderr,
"moc: Cannot create dep output file '%s'. Error: %s\n",
638 QFile::encodeName(depOutputFileName).constData(),
639 strerror(fopen_errno));
644 fprintf(depFileHandle.get(),
"%s: ",
645 escapeAndEncodeDependencyPath(depRuleName).constData());
647 QByteArrayList dependencies;
650 if (!filename.isEmpty()) {
651 dependencies.append(escapeAndEncodeDependencyPath(filename).constData());
655 for (
const QString &includeName : validIncludesFiles) {
656 dependencies.append(escapeAndEncodeDependencyPath(includeName).constData());
661 for (
const QString &pluginMetadataFile : moc.parsedPluginMetadataFiles) {
662 dependencies.append(escapeAndEncodeDependencyPath(pluginMetadataFile).constData());
667 auto includeList = pp.preprocessedIncludes.values();
668 std::sort(includeList.begin(), includeList.end());
670 for (QByteArray &includeName : includeList) {
671 dependencies.append(escapeDependencyPath(includeName));
675 const auto dependenciesJoined = dependencies.join(QByteArrayLiteral(
" \\\n "));
676 fprintf(depFileHandle.get(),
"%s\n", dependenciesJoined.constData());