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 <file>                Write output to file rather than stdout.
  -I <dir>                 Add dir to the include path for header files.
  -F <framework>           Add Mac framework to the include path for header fil
                           es.
  -E                       Preprocess only; do not generate meta object code.
  -D <macro[=def]>         Define macro, with optional definition.
  -U <macro>               Undefine macro.
  -i                       Do not generate an #include statement.
  -p <path>                Path prefix for included file.
  -f <file>                Force #include [optional <file>] (overwrite default)
                           .
  -b <file>                Prepend #include <file> (preserve default include).
  -n <which>               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 <ogoffart@woboq.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
David Faure 2013-07-24 14:07:39 +02:00 committed by The Qt Project
parent 4ff6951550
commit b508dc98a5
3 changed files with 202 additions and 226 deletions

View File

@ -50,6 +50,10 @@
#include <stdlib.h>
#include <ctype.h>
#include <qcoreapplication.h>
#include <qcommandlineoption.h>
#include <qcommandlineparser.h>
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] <header-file>\n"
" -o<file> write output to file rather than stdout\n"
" -I<dir> add dir to the include path for header files\n"
" -E preprocess only; do not generate meta object code\n"
" -D<macro>[=<def>] define macro, with optional definition\n"
" -U<macro> undefine macro\n"
" -i do not generate an #include statement\n"
" -p<path> path prefix for included file\n"
" -f[<file>] force #include, optional file name (overwrite default)\n"
" -b<file> prepend #include <file> (preserve default include)\n"
" -nn do not display notes\n"
" -nw do not display warnings\n"
" @<file> 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<QByteArray> 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);
argc = argv.count();
QCommandLineOption includePathOption(QStringLiteral("I"));
includePathOption.setDescription(QStringLiteral("Add dir to the include path for header files."));
includePathOption.setValueName(QStringLiteral("dir"));
parser.addOption(includePathOption);
for (int n = 0; n < argc; ++n) {
QByteArray arg(argv[n]);
if (arg[0] != '-') {
if (filename.isEmpty()) {
filename = arg;
continue;
}
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 <file>] (overwrite default)."));
forceIncludeOption.setValueName(QStringLiteral("file"));
parser.addOption(forceIncludeOption);
QCommandLineOption prependIncludeOption(QStringLiteral("b"));
prependIncludeOption.setDescription(QStringLiteral("Prepend #include <file> (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();
}
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();
const bool ignoreConflictingOptions = parser.isSet(ignoreConflictsOption);
output = parser.value(outputOption);
pp.preprocessOnly = parser.isSet(preprocessOption);
if (parser.isSet(noIncludeOption)) {
moc.noInclude = true;
autoInclude = false;
break;
case 'f': // produce #include statement
if (ignoreConflictingOptions)
break;
}
if (!ignoreConflictingOptions) {
if (parser.isSet(forceIncludeOption)) {
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);
foreach (const QString &include, parser.values(prependIncludeOption))
moc.includeFiles.prepend(QFile::encodeName(include));
if (parser.isSet(pathPrefixOption))
moc.includePath = QFile::encodeName(parser.value(pathPrefixOption));
}
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));
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;
}
break;
case 'D': // define macro
{
QByteArray name;
foreach (const QString &arg, parser.values(defineOption)) {
QByteArray name = arg.toLocal8Bit();
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())
if (name.isEmpty()) {
error("Missing macro name");
parser.showHelp(1);
}
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())
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);
}
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")
const QStringList noNotesCompatValues = parser.values(noNotesWarningsCompatOption);
if (parser.isSet(noNotesOption) || noNotesCompatValues.contains(QStringLiteral("n")))
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();
}
}
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

View File

@ -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();

View File

@ -75,7 +75,6 @@ public:
QList<QByteArray> frameworks;
QSet<QByteArray> preprocessedIncludes;
Macros macros;
Symbols preprocessed(const QByteArray &filename, FILE *file);
Symbols preprocessed(const QByteArray &filename, QIODevice *device);
void parseDefineArguments(Macro *m);