From b508dc98a5085df6ea44b154177fbb6d8b0e8434 Mon Sep 17 00:00:00 2001 From: David Faure Date: Wed, 24 Jul 2013 14:07:39 +0200 Subject: [PATCH] Port moc to QCommandLineParser This required special care because of @option-file where additional args can be read from. Found again one undocumented option, --ignore-option-clashes. Usage: moc [options] [header-file] [@option-file] Qt Meta Object Compiler version 67 (Qt 5.2.0) Options: -h, --help Displays this help. -v, --version Displays version information. -o Write output to file rather than stdout. -I Add dir to the include path for header files. -F Add Mac framework to the include path for header fil es. -E Preprocess only; do not generate meta object code. -D Define macro, with optional definition. -U Undefine macro. -i Do not generate an #include statement. -p Path prefix for included file. -f Force #include [optional ] (overwrite default) . -b Prepend #include (preserve default include). -n Do not display notes (-nn) or warnings (-nw). Compat ibility option. --no-notes Do not display notes. --no-warnings Do not display warnings (implies --no-notes). --ignore-option-clashes Ignore all options that conflict with compilers, lik e -pthread conflicting with moc's -p option. Arguments: [header-file] Header file to read from, otherwise stdin. [@option-file] Read additional options from option-file. Change-Id: I0dfa8e029f95ad8084832530d701e30f315df98e Reviewed-by: Olivier Goffart Reviewed-by: Thiago Macieira --- src/tools/moc/main.cpp | 420 ++++++++++++++++----------------- src/tools/moc/preprocessor.cpp | 7 - src/tools/moc/preprocessor.h | 1 - 3 files changed, 202 insertions(+), 226 deletions(-) diff --git a/src/tools/moc/main.cpp b/src/tools/moc/main.cpp index 431c8dd46c..d5218571f5 100644 --- a/src/tools/moc/main.cpp +++ b/src/tools/moc/main.cpp @@ -50,6 +50,10 @@ #include #include +#include +#include +#include + QT_BEGIN_NAMESPACE /* @@ -63,10 +67,10 @@ QT_BEGIN_NAMESPACE /tmp/abc, xyz/klm -> /tmp/abc */ -static QByteArray combinePath(const QByteArray &infile, const QByteArray &outfile) +static QByteArray combinePath(const QString &infile, const QString &outfile) { - QFileInfo inFileInfo(QDir::current(), QFile::decodeName(infile)); - QFileInfo outFileInfo(QDir::current(), QFile::decodeName(outfile)); + QFileInfo inFileInfo(QDir::current(), infile); + QFileInfo outFileInfo(QDir::current(), outfile); const QByteArray relativePath = QFile::encodeName(outFileInfo.dir().relativeFilePath(inFileInfo.filePath())); #ifdef Q_OS_WIN // It's a system limitation. @@ -83,21 +87,6 @@ void error(const char *msg = "Invalid argument") { if (msg) fprintf(stderr, "moc: %s\n", msg); - fprintf(stderr, "Usage: moc [options] \n" - " -o write output to file rather than stdout\n" - " -I add dir to the include path for header files\n" - " -E preprocess only; do not generate meta object code\n" - " -D[=] define macro, with optional definition\n" - " -U undefine macro\n" - " -i do not generate an #include statement\n" - " -p path prefix for included file\n" - " -f[] force #include, optional file name (overwrite default)\n" - " -b prepend #include (preserve default include)\n" - " -nn do not display notes\n" - " -nw do not display warnings\n" - " @ read additional options from file\n" - " -v display version of moc\n"); - exit(1); } @@ -161,9 +150,43 @@ QByteArray composePreprocessorOutput(const Symbols &symbols) { return output; } - -int runMoc(int _argc, char **_argv) +static QStringList argumentsFromCommandLineAndFile(const QStringList &arguments) { + QStringList allArguments = arguments; + int n = 0; + while (n < allArguments.count()) { + // "@file" doesn't start with a '-' so we can't use QCommandLineParser for it + if (arguments.at(n).startsWith(QLatin1Char('@'))) { + QString optionsFile = arguments.at(n); + optionsFile.remove(0, 1); + if (optionsFile.isEmpty()) { + error("The @ option requires an input file"); + return QStringList(); + } + QFile f(optionsFile); + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { + error("Cannot open options file specified with @"); + return QStringList(); + } + allArguments.removeAt(n); + while (!f.atEnd()) { + QString line = QString::fromLocal8Bit(f.readLine().trimmed()); + if (!line.isEmpty()) + allArguments.insert(n++, line); + } + } else { + ++n; + } + } + return allArguments; +} + + +int runMoc(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + QCoreApplication::setApplicationVersion(QString::fromLatin1(QT_VERSION_STR)); + bool autoInclude = true; bool defaultInclude = true; Preprocessor pp; @@ -178,199 +201,165 @@ int runMoc(int _argc, char **_argv) dummyVariadicFunctionMacro.arguments += Symbol(0, PP_IDENTIFIER, "__VA_ARGS__"); pp.macros["__attribute__"] = dummyVariadicFunctionMacro; - QByteArray filename; - QByteArray output; - FILE *in = 0; + QString filename; + QString output; + QFile in; FILE *out = 0; - bool ignoreConflictingOptions = false; - QVector argv; - argv.resize(_argc - 1); - for (int n = 1; n < _argc; ++n) - argv[n - 1] = _argv[n]; - int argc = argv.count(); + // Note that moc isn't translated. + // If you use this code as an example for a translated app, make sure to translate the strings. + QCommandLineParser parser; + parser.setApplicationDescription(QStringLiteral("Qt Meta Object Compiler version %1 (Qt %2)") + .arg(mocOutputRevision).arg(QString::fromLatin1(QT_VERSION_STR))); + parser.addHelpOption(); + parser.addVersionOption(); - for (int n = 0; n < argv.count(); ++n) { - if (argv.at(n).startsWith('@')) { - QByteArray optionsFile = argv.at(n); - optionsFile.remove(0, 1); - if (optionsFile.isEmpty()) - error("The @ option requires an input file"); - QFile f(QString::fromLatin1(optionsFile.constData())); - if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) - error("Cannot open options file specified with @"); - argv.remove(n); - while (!f.atEnd()) { - QByteArray line = f.readLine().trimmed(); - if (!line.isEmpty()) - argv.insert(n++, line); - } - } + QCommandLineOption outputOption(QStringLiteral("o")); + outputOption.setDescription(QStringLiteral("Write output to file rather than stdout.")); + outputOption.setValueName(QStringLiteral("file")); + parser.addOption(outputOption); + + QCommandLineOption includePathOption(QStringLiteral("I")); + includePathOption.setDescription(QStringLiteral("Add dir to the include path for header files.")); + includePathOption.setValueName(QStringLiteral("dir")); + parser.addOption(includePathOption); + + QCommandLineOption macFrameworkOption(QStringLiteral("F")); + macFrameworkOption.setDescription(QStringLiteral("Add Mac framework to the include path for header files.")); + macFrameworkOption.setValueName(QStringLiteral("framework")); + parser.addOption(macFrameworkOption); + + QCommandLineOption preprocessOption(QStringLiteral("E")); + preprocessOption.setDescription(QStringLiteral("Preprocess only; do not generate meta object code.")); + parser.addOption(preprocessOption); + + QCommandLineOption defineOption(QStringLiteral("D")); + defineOption.setDescription(QStringLiteral("Define macro, with optional definition.")); + defineOption.setValueName(QStringLiteral("macro[=def]")); + parser.addOption(defineOption); + + QCommandLineOption undefineOption(QStringLiteral("U")); + undefineOption.setDescription(QStringLiteral("Undefine macro.")); + undefineOption.setValueName(QStringLiteral("macro")); + parser.addOption(undefineOption); + + QCommandLineOption noIncludeOption(QStringLiteral("i")); + noIncludeOption.setDescription(QStringLiteral("Do not generate an #include statement.")); + parser.addOption(noIncludeOption); + + QCommandLineOption pathPrefixOption(QStringLiteral("p")); + pathPrefixOption.setDescription(QStringLiteral("Path prefix for included file.")); + pathPrefixOption.setValueName(QStringLiteral("path")); + parser.addOption(pathPrefixOption); + + QCommandLineOption forceIncludeOption(QStringLiteral("f")); + forceIncludeOption.setDescription(QStringLiteral("Force #include [optional ] (overwrite default).")); + forceIncludeOption.setValueName(QStringLiteral("file")); + parser.addOption(forceIncludeOption); + + QCommandLineOption prependIncludeOption(QStringLiteral("b")); + prependIncludeOption.setDescription(QStringLiteral("Prepend #include (preserve default include).")); + prependIncludeOption.setValueName(QStringLiteral("file")); + parser.addOption(prependIncludeOption); + + QCommandLineOption noNotesWarningsCompatOption(QStringLiteral("n")); + noNotesWarningsCompatOption.setDescription(QStringLiteral("Do not display notes (-nn) or warnings (-nw). Compatibility option.")); + noNotesWarningsCompatOption.setValueName(QStringLiteral("which")); + parser.addOption(noNotesWarningsCompatOption); + + QCommandLineOption noNotesOption(QStringLiteral("no-notes")); + noNotesOption.setDescription(QStringLiteral("Do not display notes.")); + parser.addOption(noNotesOption); + + QCommandLineOption noWarningsOption(QStringLiteral("no-warnings")); + noWarningsOption.setDescription(QStringLiteral("Do not display warnings (implies --no-notes).")); + parser.addOption(noWarningsOption); + + QCommandLineOption ignoreConflictsOption(QStringLiteral("ignore-option-clashes")); + ignoreConflictsOption.setDescription(QStringLiteral("Ignore all options that conflict with compilers, like -pthread conflicting with moc's -p option.")); + parser.addOption(ignoreConflictsOption); + + parser.addPositionalArgument(QStringLiteral("[header-file]"), + QStringLiteral("Header file to read from, otherwise stdin.")); + parser.addPositionalArgument(QStringLiteral("[@option-file]"), + QStringLiteral("Read additional options from option-file.")); + + const QStringList arguments = argumentsFromCommandLineAndFile(app.arguments()); + + parser.process(arguments); + + const QStringList files = parser.positionalArguments(); + if (files.count() > 1) { + error("Too many input files specified"); + parser.showHelp(1); + } else if (!files.isEmpty()) { + filename = files.first(); } - argc = argv.count(); - - for (int n = 0; n < argc; ++n) { - QByteArray arg(argv[n]); - if (arg[0] != '-') { - if (filename.isEmpty()) { - filename = arg; - continue; - } - error("Too many input files specified"); - } - QByteArray opt = arg.mid(1); - bool more = (opt.size() > 1); - switch (opt[0]) { - case 'o': // output redirection - if (!more) { - if (!(n < argc-1)) - error("Missing output file name"); - output = argv[++n]; - } else - output = opt.mid(1); - break; - case 'E': // only preprocessor - pp.preprocessOnly = true; - break; - case 'i': // no #include statement - if (more) - error(); - moc.noInclude = true; + const bool ignoreConflictingOptions = parser.isSet(ignoreConflictsOption); + output = parser.value(outputOption); + pp.preprocessOnly = parser.isSet(preprocessOption); + if (parser.isSet(noIncludeOption)) { + moc.noInclude = true; + autoInclude = false; + } + if (!ignoreConflictingOptions) { + if (parser.isSet(forceIncludeOption)) { + moc.noInclude = false; autoInclude = false; - break; - case 'f': // produce #include statement - if (ignoreConflictingOptions) - break; - moc.noInclude = false; - autoInclude = false; - if (opt[1]) { // -fsomething.h - moc.includeFiles.append(opt.mid(1)); + foreach (const QString &include, parser.values(forceIncludeOption)) { + moc.includeFiles.append(QFile::encodeName(include)); defaultInclude = false; - } - break; - case 'b': - if (ignoreConflictingOptions) - break; - if (!more) { - if (!(n < argc-1)) - error("Missing file name for the -b option."); - moc.includeFiles.prepend(argv[++n]); - } else if (opt[1]) { - moc.includeFiles.prepend(opt.mid(1)); - } - break; - case 'p': // include file path - if (ignoreConflictingOptions) - break; - if (!more) { - if (!(n < argc-1)) - error("Missing path name for the -p option."); - moc.includePath = argv[++n]; - } else { - moc.includePath = opt.mid(1); - } - break; - case 'I': // produce #include statement - if (!more) { - if (!(n < argc-1)) - error("Missing path name for the -I option."); - pp.includes += Preprocessor::IncludePath(argv[++n]); - } else { - pp.includes += Preprocessor::IncludePath(opt.mid(1)); - } - break; - case 'F': // minimalistic framework support for the mac - if (!more) { - if (!(n < argc-1)) - error("Missing path name for the -F option."); - Preprocessor::IncludePath p(argv[++n]); - p.isFrameworkPath = true; - pp.includes += p; - } else { - Preprocessor::IncludePath p(opt.mid(1)); - p.isFrameworkPath = true; - pp.includes += p; - } - break; - case 'D': // define macro - { - QByteArray name; - QByteArray value("1"); - if (!more) { - if (n < argc-1) - name = argv[++n]; - } else - name = opt.mid(1); - int eq = name.indexOf('='); - if (eq >= 0) { - value = name.mid(eq + 1); - name = name.left(eq); - } - if (name.isEmpty()) - error("Missing macro name"); - Macro macro; - macro.symbols += Symbol(0, PP_IDENTIFIER, value); - pp.macros.insert(name, macro); - - } - break; - case 'U': - { - QByteArray macro; - if (!more) { - if (n < argc-1) - macro = argv[++n]; - } else - macro = opt.mid(1); - if (macro.isEmpty()) - error("Missing macro name"); - pp.macros.remove(macro); - - } - break; - case 'v': // version number - if (more && opt != "version") - error(); - fprintf(stderr, "Qt Meta Object Compiler version %d (Qt %s)\n", - mocOutputRevision, QT_VERSION_STR); - return 1; - case 'n': // don't display warnings - if (ignoreConflictingOptions) - break; - if (opt == "nw") - moc.displayWarnings = moc.displayNotes = false; - else if (opt == "nn") - moc.displayNotes = false; - else - error(); - break; - case 'h': // help - if (more && opt != "help") - error(); - else - error(0); // 0 means usage only - break; - case '-': - if (more && arg == "--ignore-option-clashes") { - // -- ignore all following moc specific options that conflict - // with for example gcc, like -pthread conflicting with moc's - // -p option. - ignoreConflictingOptions = true; - break; - } - // fall through - default: - error(); + } } + foreach (const QString &include, parser.values(prependIncludeOption)) + moc.includeFiles.prepend(QFile::encodeName(include)); + if (parser.isSet(pathPrefixOption)) + moc.includePath = QFile::encodeName(parser.value(pathPrefixOption)); } - + foreach (const QString &path, parser.values(includePathOption)) + pp.includes += Preprocessor::IncludePath(QFile::encodeName(path)); + foreach (const QString &path, parser.values(macFrameworkOption)) { + // minimalistic framework support for the mac + Preprocessor::IncludePath p(QFile::encodeName(path)); + p.isFrameworkPath = true; + pp.includes += p; + } + foreach (const QString &arg, parser.values(defineOption)) { + QByteArray name = arg.toLocal8Bit(); + QByteArray value("1"); + int eq = name.indexOf('='); + if (eq >= 0) { + value = name.mid(eq + 1); + name = name.left(eq); + } + if (name.isEmpty()) { + error("Missing macro name"); + parser.showHelp(1); + } + Macro macro; + macro.symbols += Symbol(0, PP_IDENTIFIER, value); + pp.macros.insert(name, macro); + } + foreach (const QString &arg, parser.values(undefineOption)) { + QByteArray macro = arg.toLocal8Bit(); + if (macro.isEmpty()) { + error("Missing macro name"); + parser.showHelp(1); + } + pp.macros.remove(macro); + } + const QStringList noNotesCompatValues = parser.values(noNotesWarningsCompatOption); + if (parser.isSet(noNotesOption) || noNotesCompatValues.contains(QStringLiteral("n"))) + moc.displayNotes = false; + if (parser.isSet(noWarningsOption) || noNotesCompatValues.contains(QStringLiteral("w"))) + moc.displayWarnings = moc.displayNotes = false; if (autoInclude) { - int spos = filename.lastIndexOf(QDir::separator().toLatin1()); - int ppos = filename.lastIndexOf('.'); + int spos = filename.lastIndexOf(QDir::separator()); + int ppos = filename.lastIndexOf(QLatin1Char('.')); // spos >= -1 && ppos > spos => ppos >= 0 - moc.noInclude = (ppos > spos && tolower(filename[ppos + 1]) != 'h'); + moc.noInclude = (ppos > spos && filename[ppos + 1].toLower() != QLatin1Char('h')); } if (defaultInclude) { if (moc.includePath.isEmpty()) { @@ -378,7 +367,7 @@ int runMoc(int _argc, char **_argv) if (output.size()) moc.includeFiles.append(combinePath(filename, output)); else - moc.includeFiles.append(filename); + moc.includeFiles.append(QFile::encodeName(filename)); } } else { moc.includeFiles.append(combinePath(filename, filename)); @@ -386,27 +375,22 @@ int runMoc(int _argc, char **_argv) } if (filename.isEmpty()) { - filename = "standard input"; - in = stdin; + filename = QStringLiteral("standard input"); + in.open(stdin, QIODevice::ReadOnly); } else { -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (fopen_s(&in, filename.data(), "rb")) { -#else - in = fopen(filename.data(), "rb"); - if (!in) { -#endif - fprintf(stderr, "moc: %s: No such file\n", filename.constData()); + in.setFileName(filename); + if (!in.open(QIODevice::ReadOnly)) { + fprintf(stderr, "moc: %s: No such file\n", qPrintable(filename)); return 1; } - moc.filename = filename; + moc.filename = filename.toLocal8Bit(); } - moc.currentFilenames.push(filename); + moc.currentFilenames.push(filename.toLocal8Bit()); moc.includes = pp.includes; // 1. preprocess - moc.symbols = pp.preprocessed(moc.filename, in); - fclose(in); + moc.symbols = pp.preprocessed(moc.filename, &in); if (!pp.preprocessOnly) { // 2. parse @@ -417,13 +401,13 @@ int runMoc(int _argc, char **_argv) if (output.size()) { // output file specified #if defined(_MSC_VER) && _MSC_VER >= 1400 - if (fopen_s(&out, output.data(), "w")) + if (fopen_s(&out, QFile::encodeName(output).constData(), "w")) #else - out = fopen(output.data(), "w"); // create output file + out = fopen(QFile::encodeName(output).constData(), "w"); // create output file if (!out) #endif { - fprintf(stderr, "moc: Cannot create %s\n", output.constData()); + fprintf(stderr, "moc: Cannot create %s\n", QFile::encodeName(output).constData()); return 1; } } else { // use stdout diff --git a/src/tools/moc/preprocessor.cpp b/src/tools/moc/preprocessor.cpp index 06758e67bd..3615262b67 100644 --- a/src/tools/moc/preprocessor.cpp +++ b/src/tools/moc/preprocessor.cpp @@ -1172,13 +1172,6 @@ void Preprocessor::preprocess(const QByteArray &filename, Symbols &preprocessed) currentFilenames.pop(); } -Symbols Preprocessor::preprocessed(const QByteArray &filename, FILE *file) -{ - QFile qfile; - qfile.open(file, QFile::ReadOnly); - return preprocessed(filename, &qfile); -} - Symbols Preprocessor::preprocessed(const QByteArray &filename, QIODevice *file) { QByteArray input = file->readAll(); diff --git a/src/tools/moc/preprocessor.h b/src/tools/moc/preprocessor.h index 72d02a3292..dc7c98de85 100644 --- a/src/tools/moc/preprocessor.h +++ b/src/tools/moc/preprocessor.h @@ -75,7 +75,6 @@ public: QList frameworks; QSet preprocessedIncludes; Macros macros; - Symbols preprocessed(const QByteArray &filename, FILE *file); Symbols preprocessed(const QByteArray &filename, QIODevice *device); void parseDefineArguments(Macro *m);