src/tools/moc/main.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tools/moc/main.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,445 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "preprocessor.h"
+#include "moc.h"
+#include "outputrevision.h"
+#include "../../corelib/global/qconfig.cpp"
+#include <QFile>
+#include <QFileInfo>
+#include <QDir>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+QT_BEGIN_NAMESPACE
+
+/*
+    This function looks at two file names and returns the name of the
+    infile with a path relative to outfile.
+
+    Examples:
+
+        /tmp/abc, /tmp/bcd -> abc
+        xyz/a/bc, xyz/b/ac -> ../a/bc
+        /tmp/abc, xyz/klm -> /tmp/abc
+ */
+
+static QByteArray combinePath(const char *infile, const char *outfile)
+{
+    QFileInfo inFileInfo(QDir::current(), QFile::decodeName(infile));
+    QFileInfo outFileInfo(QDir::current(), QFile::decodeName(outfile));
+    int numCommonComponents = 0;
+
+    QStringList inSplitted = inFileInfo.dir().canonicalPath().split(QLatin1Char('/'));
+    QStringList outSplitted = outFileInfo.dir().canonicalPath().split(QLatin1Char('/'));
+
+    while (!inSplitted.isEmpty() && !outSplitted.isEmpty() &&
+            inSplitted.first() == outSplitted.first()) {
+        inSplitted.removeFirst();
+        outSplitted.removeFirst();
+        numCommonComponents++;
+    }
+
+    if (numCommonComponents < 2)
+        /*
+          The paths don't have the same drive, or they don't have the
+          same root directory. Use an absolute path.
+        */
+        return QFile::encodeName(inFileInfo.absoluteFilePath());
+    /*
+       The paths have something in common. Use a path relative to
+       the output file.
+     */
+    while (!outSplitted.isEmpty()) {
+        outSplitted.removeFirst();
+        inSplitted.prepend(QLatin1String(".."));
+    }
+    inSplitted.append(inFileInfo.fileName());
+    return QFile::encodeName(inSplitted.join(QLatin1String("/")));
+}
+
+
+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\n"
+            "  -nw                do not display warnings\n"
+            "  @<file>            read additional options from file\n"
+            "  -v                 display version of moc\n");
+    exit(1);
+}
+
+
+static inline bool hasNext(const Symbols &symbols, int i)
+{ return (i < symbols.size()); }
+
+static inline const Symbol &next(const Symbols &symbols, int &i)
+{ return symbols.at(i++); }
+
+
+QByteArray composePreprocessorOutput(const Symbols &symbols) {
+    QByteArray output;
+    int lineNum = 1;
+    Token last = PP_NOTOKEN;
+    Token secondlast = last;
+    int i = 0;
+    while (hasNext(symbols, i)) {
+        Symbol sym = next(symbols, i);
+        switch (sym.token) {
+        case PP_NEWLINE:
+        case PP_WHITESPACE:
+            if (last != PP_WHITESPACE) {
+                secondlast = last;
+                last = PP_WHITESPACE;
+                output += ' ';
+            }
+            continue;
+        case PP_STRING_LITERAL:
+            if (last == PP_STRING_LITERAL)
+                output.chop(1);
+            else if (secondlast == PP_STRING_LITERAL && last == PP_WHITESPACE)
+                output.chop(2);
+            else
+                break;
+            output += sym.lexem().mid(1);
+            secondlast = last;
+            last = PP_STRING_LITERAL;
+            continue;
+        case MOC_INCLUDE_BEGIN:
+            lineNum = 0;
+            continue;
+        case MOC_INCLUDE_END:
+            lineNum = sym.lineNum;
+            continue;
+        default:
+            break;
+        }
+        secondlast = last;
+        last = sym.token;
+
+        const int padding = sym.lineNum - lineNum;
+        if (padding > 0) {
+            output.resize(output.size() + padding);
+            qMemSet(output.data() + output.size() - padding, '\n', padding);
+            lineNum = sym.lineNum;
+        }
+
+        output += sym.lexem();
+    }
+
+    return output;
+}
+
+
+int runMoc(int _argc, char **_argv)
+{
+    bool autoInclude = true;
+    Preprocessor pp;
+    Moc moc;
+    pp.macros["Q_MOC_RUN"];
+    pp.macros["__cplusplus"];
+    QByteArray filename;
+    QByteArray output;
+    FILE *in = 0;
+    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();
+
+    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);
+            }
+        }
+    }
+
+    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;
+            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));
+            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")
+                error();
+            moc.displayWarnings = false;
+            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 (autoInclude) {
+        int ppos = filename.lastIndexOf('.');
+        moc.noInclude = (ppos >= 0
+                         && tolower(filename[ppos + 1]) != 'h'
+                         && tolower(filename[ppos + 1]) != QDir::separator().toLatin1()
+                        );
+    }
+    if (moc.includeFiles.isEmpty()) {
+        if (moc.includePath.isEmpty()) {
+            if (filename.size()) {
+                if (output.size())
+                    moc.includeFiles.append(combinePath(filename, output));
+                else
+                    moc.includeFiles.append(filename);
+            }
+        } else {
+            moc.includeFiles.append(combinePath(filename, filename));
+        }
+    }
+
+    if (filename.isEmpty()) {
+        filename = "standard input";
+        in = stdin;
+    } 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", (const char*)filename);
+            return 1;
+        }
+        moc.filename = filename;
+    }
+
+    moc.currentFilenames.push(filename);
+
+    // 1. preprocess
+    moc.symbols = pp.preprocessed(moc.filename, in);
+    fclose(in);
+
+    if (!pp.preprocessOnly) {
+        // 2. parse
+        moc.parse();
+    }
+
+    // 3. and output meta object code
+
+    if (output.size()) { // output file specified
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+        if (fopen_s(&out, output.data(), "w"))
+#else
+        out = fopen(output.data(), "w"); // create output file
+        if (!out)
+#endif
+        {
+            fprintf(stderr, "moc: Cannot create %s\n", (const char*)output);
+            return 1;
+        }
+    } else { // use stdout
+        out = stdout;
+    }
+
+    if (pp.preprocessOnly) {
+        fprintf(out, "%s\n", composePreprocessorOutput(moc.symbols).constData());
+    } else {
+        if (moc.classList.isEmpty())
+            moc.warning("No relevant classes found. No output generated.");
+        else
+            moc.generate(out);
+    }
+
+    if (output.size())
+        fclose(out);
+
+    return 0;
+}
+
+QT_END_NAMESPACE
+
+int main(int _argc, char **_argv)
+{
+    return QT_PREPEND_NAMESPACE(runMoc)(_argc, _argv);
+}