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."));
208 outputOption.setValueName(QStringLiteral(
"file"));
209 outputOption.setFlags(QCommandLineOption::ShortOptionStyle);
210 parser.addOption(outputOption);
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);
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);
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."));
230 defineOption.setValueName(QStringLiteral(
"macro[=def]"));
231 defineOption.setFlags(QCommandLineOption::ShortOptionStyle);
232 parser.addOption(defineOption);
235 undefineOption.setDescription(QStringLiteral(
"Undefine macro."));
236 undefineOption.setValueName(QStringLiteral(
"macro"));
237 undefineOption.setFlags(QCommandLineOption::ShortOptionStyle);
238 parser.addOption(undefineOption);
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);
247 compilerFlavorOption.setDescription(QStringLiteral(
"Set the compiler flavor: either \"msvc\" or \"unix\"."));
248 compilerFlavorOption.setValueName(QStringLiteral(
"flavor"));
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."));
257 pathPrefixOption.setValueName(QStringLiteral(
"path"));
258 pathPrefixOption.setFlags(QCommandLineOption::ShortOptionStyle);
259 parser.addOption(pathPrefixOption);
262 forceIncludeOption.setDescription(QStringLiteral(
"Force #include <file> (overwrite default)."));
263 forceIncludeOption.setValueName(QStringLiteral(
"file"));
264 forceIncludeOption.setFlags(QCommandLineOption::ShortOptionStyle);
265 parser.addOption(forceIncludeOption);
268 prependIncludeOption.setDescription(QStringLiteral(
"Prepend #include <file> (preserve default include)."));
269 prependIncludeOption.setValueName(QStringLiteral(
"file"));
270 prependIncludeOption.setFlags(QCommandLineOption::ShortOptionStyle);
271 parser.addOption(prependIncludeOption);
274 includeOption.setDescription(QStringLiteral(
"Parse <file> as an #include before the main source(s)."));
275 includeOption.setValueName(QStringLiteral(
"file"));
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 noNotesOption.setDescription(QStringLiteral(
"Do not display notes."));
290 parser.addOption(noNotesOption);
293 noWarningsOption.setDescription(QStringLiteral(
"Do not display warnings (implies --no-notes)."));
294 parser.addOption(noWarningsOption);
297 ignoreConflictsOption.setDescription(QStringLiteral(
"Ignore all options that conflict with compilers, like -pthread conflicting with moc's -p option."));
298 parser.addOption(ignoreConflictsOption);
301 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."));
302 parser.addOption(jsonOption);
305 debugIncludesOption.setDescription(QStringLiteral(
"Display debug messages of each considered include path."));
306 parser.addOption(debugIncludesOption);
309 collectOption.setDescription(QStringLiteral(
"Instead of processing C++ code, collect previously generated JSON output into a single file."));
310 parser.addOption(collectOption);
313 depFileOption.setDescription(
314 QStringLiteral(
"Output a Make-style dep file for build system consumption."));
315 parser.addOption(depFileOption);
318 depFilePathOption.setDescription(QStringLiteral(
"Path where to write the dep file."));
319 depFilePathOption.setValueName(QStringLiteral(
"file"));
320 parser.addOption(depFilePathOption);
323 depFileRuleNameOption.setDescription(
324 QStringLiteral(
"The rule name (first line) of the dep file."));
325 depFileRuleNameOption.setValueName(QStringLiteral(
"rule name"));
326 parser.addOption(depFileRuleNameOption);
328 QCommandLineOption requireCompleTypesOption(QStringLiteral(
"require-complete-types"));
329 requireCompleTypesOption.setDescription(QStringLiteral(
"Require complete types for better performance"));
330 parser.addOption(requireCompleTypesOption);
332 parser.addPositionalArgument(QStringLiteral(
"[header-file]"),
333 QStringLiteral(
"Header file to read from, otherwise stdin."));
334 parser.addPositionalArgument(QStringLiteral(
"[@option-file]"),
335 QStringLiteral(
"Read additional options from option-file."));
336 parser.addPositionalArgument(QStringLiteral(
"[MOC generated json file]"),
337 QStringLiteral(
"MOC generated json output"));
339 bool hasOptionFiles =
false;
340 const QStringList arguments = argumentsFromCommandLineAndFile(app.arguments(), hasOptionFiles);
341 if (arguments.isEmpty())
344 parser.process(arguments);
347 moc.activeQtMode = parser.isSet(activeQtMode);
349 const QStringList files = parser.positionalArguments();
350 output = parser.value(outputOption);
351 if (parser.isSet(collectOption))
352 return collectJson(files, output, hasOptionFiles);
354 if (files.size() > 1) {
355 error(
qPrintable(
"Too many input files specified: '"_L1 + files.join(
"' '"_L1) + u'\''));
357 }
else if (!files.isEmpty()) {
358 filename = files.first();
361 const bool ignoreConflictingOptions = parser.isSet(ignoreConflictsOption);
362 pp.preprocessOnly = parser.isSet(preprocessOption);
363 pp.setDebugIncludes(parser.isSet(debugIncludesOption));
364 if (parser.isSet(noIncludeOption)) {
368 if (parser.isSet(requireCompleTypesOption))
370 if (!ignoreConflictingOptions) {
371 if (parser.isSet(forceIncludeOption)) {
374 const auto forceIncludes = parser.values(forceIncludeOption);
375 for (
const QString &include : forceIncludes) {
376 moc.includeFiles.append(QFile::encodeName(include));
377 defaultInclude =
false;
380 const auto prependIncludes = parser.values(prependIncludeOption);
381 for (
const QString &include : prependIncludes)
382 moc.includeFiles.prepend(QFile::encodeName(include));
383 if (parser.isSet(pathPrefixOption))
384 moc.includePath = QFile::encodeName(parser.value(pathPrefixOption));
387 const auto includePaths = parser.values(includePathOption);
388 for (
const QString &path : includePaths)
389 pp.includes += Preprocessor::IncludePath(QFile::encodeName(path));
390 QString compilerFlavor = parser.value(compilerFlavorOption);
391 if (compilerFlavor.isEmpty() || compilerFlavor ==
"unix"_L1) {
395 const auto cpath = qgetenv(
"CPATH").split(QDir::listSeparator().toLatin1());
396 for (
const QByteArray &p : cpath)
397 pp.includes += Preprocessor::IncludePath(p);
398 const auto cplus_include_path = qgetenv(
"CPLUS_INCLUDE_PATH").split(QDir::listSeparator().toLatin1());
399 for (
const QByteArray &p : cplus_include_path)
400 pp.includes += Preprocessor::IncludePath(p);
401 }
else if (compilerFlavor ==
"msvc"_L1) {
403 const auto include = qgetenv(
"INCLUDE").split(QDir::listSeparator().toLatin1());
404 for (
const QByteArray &p : include)
405 pp.includes += Preprocessor::IncludePath(p);
407 error(
qPrintable(
"Unknown compiler flavor '"_L1 + compilerFlavor +
408 "'; valid values are: msvc, unix."_L1));
412 const auto macFrameworks = parser.values(macFrameworkOption);
413 for (
const QString &path : macFrameworks) {
415 Preprocessor::IncludePath p(QFile::encodeName(path));
416 p.isFrameworkPath =
true;
419 const auto defines = parser.values(defineOption);
420 for (
const QString &arg : defines) {
421 QByteArray name = arg.toLocal8Bit();
422 QByteArray value(
"1");
423 const qsizetype eq = name.indexOf(
'=');
425 value = name.mid(eq + 1);
426 name = name.left(eq);
428 if (name.isEmpty()) {
429 error(
"Missing macro name");
433 macro.symbols = Preprocessor::tokenize(value, 1, Preprocessor::TokenizeDefine);
434 macro.symbols.removeLast();
435 pp.macros.insert(name, macro);
437 const auto undefines = parser.values(undefineOption);
438 for (
const QString &arg : undefines) {
439 QByteArray macro = arg.toLocal8Bit();
440 if (macro.isEmpty()) {
441 error(
"Missing macro name");
444 pp.macros.remove(macro);
446 const QStringList noNotesCompatValues = parser.values(noNotesWarningsCompatOption);
447 if (parser.isSet(noNotesOption) || noNotesCompatValues.contains(
"n"_L1))
448 moc.displayNotes =
false;
449 if (parser.isSet(noWarningsOption) || noNotesCompatValues.contains(
"w"_L1))
450 moc.displayWarnings = moc.displayNotes =
false;
453 qsizetype spos = filename.lastIndexOf(QDir::separator());
454 qsizetype ppos = filename.lastIndexOf(u'.');
456 moc
.noInclude = (ppos > spos && filename.at(ppos + 1).toLower() != u'h');
458 if (defaultInclude) {
459 if (moc.includePath.isEmpty()) {
460 if (filename.size()) {
462 moc.includeFiles.append(combinePath(filename, output));
464 moc.includeFiles.append(QFile::encodeName(filename));
467 moc.includeFiles.append(combinePath(filename, filename));
471 if (filename.isEmpty()) {
472 filename = QStringLiteral(
"standard input");
473 if (!in.open(stdin, QIODevice::ReadOnly)) {
474 fprintf(stderr,
"moc: cannot open standard input: %s\n",
qPrintable(in.errorString()));
478 in.setFileName(filename);
479 if (!in.open(QIODevice::ReadOnly)) {
480 fprintf(stderr,
"moc: cannot open %s: %s\n",
qPrintable(filename),
qPrintable(in.errorString()));
483 moc.filename = filename.toLocal8Bit();
486 const auto metadata = parser.values(metadataOption);
487 for (
const QString &md : metadata) {
488 qsizetype split = md.indexOf(u'=');
489 QString key = md.left(split);
490 QString value = md.mid(split + 1);
492 if (split == -1 || key.isEmpty() || value.isEmpty()) {
493 error(
"missing key or value for option '-M'");
494 }
else if (key.indexOf(u'.') != -1) {
497 error(
"A key cannot contain the letter '.' for option '-M'");
499 QJsonArray array = moc.metaArgs.value(key);
501 moc.metaArgs.insert(key, array);
505 moc.currentFilenames.push(filename.toLocal8Bit());
506 moc.includes = pp.includes;
508 if (Q_UNLIKELY(parser.isSet(debugIncludesOption))) {
509 fprintf(stderr,
"debug-includes: include search list:\n");
511 for (
auto &includePath : pp.includes) {
512 fprintf(stderr,
"debug-includes: '%s' framework: %d \n",
513 includePath.path.constData(),
514 includePath.isFrameworkPath);
516 fprintf(stderr,
"debug-includes: end of search list.\n");
520 const auto includeFiles = parser.values(includeOption);
521 QStringList validIncludesFiles;
522 for (
const QString &includeName : includeFiles) {
523 QByteArray rawName = pp.resolveInclude(QFile::encodeName(includeName), moc.filename);
524 if (rawName.isEmpty()) {
525 fprintf(stderr,
"Warning: Failed to resolve include \"%s\" for moc file %s\n",
526 includeName.toLocal8Bit().constData(),
527 moc.filename.isEmpty() ?
"<standard input>" : moc.filename.constData());
529 QFile f(QFile::decodeName(rawName));
530 if (f.open(QIODevice::ReadOnly)) {
531 moc.symbols += Symbol(0, MOC_INCLUDE_BEGIN, rawName);
532 moc.symbols += pp.preprocessed(rawName, &f);
533 moc.symbols += Symbol(0, MOC_INCLUDE_END, rawName);
534 validIncludesFiles.append(includeName);
536 fprintf(stderr,
"Warning: Cannot open %s included by moc file %s: %s\n",
538 moc.filename.isEmpty() ?
"<standard input>" : moc.filename.constData(),
539 f.errorString().toLocal8Bit().constData());
543 moc.symbols += pp.preprocessed(moc.filename, &in);
554 bool outputToFile =
true;
556 out = openFileForWriting(output);
559 const auto fopen_errno = errno;
560 fprintf(stderr,
"moc: Cannot create %s. Error: %s\n",
561 QFile::encodeName(output).constData(),
562 strerror(fopen_errno));
566 if (parser.isSet(jsonOption)) {
567 const QString jsonOutputFileName = output +
".json"_L1;
568 jsonOutput = openFileForWriting(jsonOutputFileName);
570 const auto fopen_errno = errno;
571 fprintf(stderr,
"moc: Cannot create JSON output file %s. Error: %s\n",
572 QFile::encodeName(jsonOutputFileName).constData(),
573 strerror(fopen_errno));
578 outputToFile =
false;
582 fprintf(out.get(),
"%s\n", composePreprocessorOutput(moc.symbols).constData());
583 }
else if (moc.classList.isEmpty()) {
584 moc.note(
"No relevant classes found. No output generated.");
586 const QJsonDocument jsonDoc(QJsonObject {
587 {
"outputRevision"_L1, mocOutputRevision },
588 {
"inputFile"_L1, QLatin1StringView(moc.strippedFileName()) }
590 fputs(jsonDoc.toJson().constData(), jsonOutput.get());
598 if (parser.isSet(depFileOption)) {
600 QString depOutputFileName;
601 QString depRuleName = output;
603 if (parser.isSet(depFileRuleNameOption))
604 depRuleName = parser.value(depFileRuleNameOption);
606 if (parser.isSet(depFilePathOption)) {
607 depOutputFileName = parser.value(depFilePathOption);
608 }
else if (outputToFile) {
609 depOutputFileName = output +
".d"_L1;
611 fprintf(stderr,
"moc: Writing to stdout, but no depfile path specified.\n");
614 File depFileHandle = openFileForWriting(depOutputFileName);
615 if (!depFileHandle) {
616 const auto fopen_errno = errno;
617 fprintf(stderr,
"moc: Cannot create dep output file '%s'. Error: %s\n",
618 QFile::encodeName(depOutputFileName).constData(),
619 strerror(fopen_errno));
624 fprintf(depFileHandle.get(),
"%s: ",
625 escapeAndEncodeDependencyPath(depRuleName).constData());
627 QByteArrayList dependencies;
630 if (!filename.isEmpty()) {
631 dependencies.append(escapeAndEncodeDependencyPath(filename).constData());
635 for (
const QString &includeName : validIncludesFiles) {
636 dependencies.append(escapeAndEncodeDependencyPath(includeName).constData());
641 for (
const QString &pluginMetadataFile : moc.parsedPluginMetadataFiles) {
642 dependencies.append(escapeAndEncodeDependencyPath(pluginMetadataFile).constData());
647 auto includeList = pp.preprocessedIncludes.values();
648 std::sort(includeList.begin(), includeList.end());
650 for (QByteArray &includeName : includeList) {
651 dependencies.append(escapeDependencyPath(includeName));
655 const auto dependenciesJoined = dependencies.join(QByteArrayLiteral(
" \\\n "));
656 fprintf(depFileHandle.get(),
"%s\n", dependenciesJoined.constData());