Revert "Allow moc to handle symbols that have been redefined."
[profile/ivi/qtbase.git] / src / tools / moc / main.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "preprocessor.h"
43 #include "moc.h"
44 #include "outputrevision.h"
45 #include <qconfig.cpp>
46 #include <QFile>
47 #include <QFileInfo>
48 #include <QDir>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <ctype.h>
52
53 QT_BEGIN_NAMESPACE
54
55 /*
56     This function looks at two file names and returns the name of the
57     infile with a path relative to outfile.
58
59     Examples:
60
61         /tmp/abc, /tmp/bcd -> abc
62         xyz/a/bc, xyz/b/ac -> ../a/bc
63         /tmp/abc, xyz/klm -> /tmp/abc
64  */
65
66 static QByteArray combinePath(const QByteArray &infile, const QByteArray &outfile)
67 {
68     QFileInfo inFileInfo(QDir::current(), QFile::decodeName(infile));
69     QFileInfo outFileInfo(QDir::current(), QFile::decodeName(outfile));
70     const QByteArray relativePath = QFile::encodeName(outFileInfo.dir().relativeFilePath(inFileInfo.filePath()));
71 #ifdef Q_OS_WIN
72     // It's a system limitation.
73     // It depends on the Win API function which is used by the program to open files.
74     // cl apparently uses the functions that have the MAX_PATH limitation.
75     if (outFileInfo.dir().absolutePath().length() + relativePath.length() + 1 >= 260)
76         return QFile::encodeName(inFileInfo.absoluteFilePath());
77 #endif
78     return relativePath;
79 }
80
81
82 void error(const char *msg = "Invalid argument")
83 {
84     if (msg)
85         fprintf(stderr, "moc: %s\n", msg);
86     fprintf(stderr, "Usage: moc [options] <header-file>\n"
87             "  -o<file>           write output to file rather than stdout\n"
88             "  -I<dir>            add dir to the include path for header files\n"
89             "  -E                 preprocess only; do not generate meta object code\n"
90             "  -D<macro>[=<def>]  define macro, with optional definition\n"
91             "  -U<macro>          undefine macro\n"
92             "  -i                 do not generate an #include statement\n"
93             "  -p<path>           path prefix for included file\n"
94             "  -f[<file>]         force #include, optional file name (overwrite default)\n"
95             "  -b<file>           prepend #include <file> (preserve default include)\n"
96             "  -nn                do not display notes\n"
97             "  -nw                do not display warnings\n"
98             "  @<file>            read additional options from file\n"
99             "  -v                 display version of moc\n");
100     exit(1);
101 }
102
103
104 static inline bool hasNext(const Symbols &symbols, int i)
105 { return (i < symbols.size()); }
106
107 static inline const Symbol &next(const Symbols &symbols, int &i)
108 { return symbols.at(i++); }
109
110
111 QByteArray composePreprocessorOutput(const Symbols &symbols) {
112     QByteArray output;
113     int lineNum = 1;
114     Token last = PP_NOTOKEN;
115     Token secondlast = last;
116     int i = 0;
117     while (hasNext(symbols, i)) {
118         Symbol sym = next(symbols, i);
119         switch (sym.token) {
120         case PP_NEWLINE:
121         case PP_WHITESPACE:
122             if (last != PP_WHITESPACE) {
123                 secondlast = last;
124                 last = PP_WHITESPACE;
125                 output += ' ';
126             }
127             continue;
128         case PP_STRING_LITERAL:
129             if (last == PP_STRING_LITERAL)
130                 output.chop(1);
131             else if (secondlast == PP_STRING_LITERAL && last == PP_WHITESPACE)
132                 output.chop(2);
133             else
134                 break;
135             output += sym.lexem().mid(1);
136             secondlast = last;
137             last = PP_STRING_LITERAL;
138             continue;
139         case MOC_INCLUDE_BEGIN:
140             lineNum = 0;
141             continue;
142         case MOC_INCLUDE_END:
143             lineNum = sym.lineNum;
144             continue;
145         default:
146             break;
147         }
148         secondlast = last;
149         last = sym.token;
150
151         const int padding = sym.lineNum - lineNum;
152         if (padding > 0) {
153             output.resize(output.size() + padding);
154             memset(output.data() + output.size() - padding, '\n', padding);
155             lineNum = sym.lineNum;
156         }
157
158         output += sym.lexem();
159     }
160
161     return output;
162 }
163
164
165 int runMoc(int _argc, char **_argv)
166 {
167     bool autoInclude = true;
168     bool defaultInclude = true;
169     Preprocessor pp;
170     Moc moc;
171     pp.macros["Q_MOC_RUN"];
172     pp.macros["__cplusplus"];
173     QByteArray filename;
174     QByteArray output;
175     FILE *in = 0;
176     FILE *out = 0;
177     bool ignoreConflictingOptions = false;
178
179     QVector<QByteArray> argv;
180     argv.resize(_argc - 1);
181     for (int n = 1; n < _argc; ++n)
182         argv[n - 1] = _argv[n];
183     int argc = argv.count();
184
185     for (int n = 0; n < argv.count(); ++n) {
186         if (argv.at(n).startsWith('@')) {
187             QByteArray optionsFile = argv.at(n);
188             optionsFile.remove(0, 1);
189             if (optionsFile.isEmpty())
190                 error("The @ option requires an input file");
191             QFile f(QString::fromLatin1(optionsFile.constData()));
192             if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
193                 error("Cannot open options file specified with @");
194             argv.remove(n);
195             while (!f.atEnd()) {
196                 QByteArray line = f.readLine().trimmed();
197                 if (!line.isEmpty())
198                     argv.insert(n++, line);
199             }
200         }
201     }
202
203     argc = argv.count();
204
205     for (int n = 0; n < argc; ++n) {
206         QByteArray arg(argv[n]);
207         if (arg[0] != '-') {
208             if (filename.isEmpty()) {
209                 filename = arg;
210                 continue;
211             }
212             error("Too many input files specified");
213         }
214         QByteArray opt = arg.mid(1);
215         bool more = (opt.size() > 1);
216         switch (opt[0]) {
217         case 'o': // output redirection
218             if (!more) {
219                 if (!(n < argc-1))
220                     error("Missing output file name");
221                 output = argv[++n];
222             } else
223                 output = opt.mid(1);
224             break;
225         case 'E': // only preprocessor
226             pp.preprocessOnly = true;
227             break;
228         case 'i': // no #include statement
229             if (more)
230                 error();
231             moc.noInclude        = true;
232             autoInclude = false;
233             break;
234         case 'f': // produce #include statement
235             if (ignoreConflictingOptions)
236                 break;
237             moc.noInclude        = false;
238             autoInclude = false;
239             if (opt[1]) {                       // -fsomething.h
240                 moc.includeFiles.append(opt.mid(1));
241                 defaultInclude = false;
242             }
243             break;
244         case 'b':
245             if (ignoreConflictingOptions)
246                 break;
247             if (!more) {
248                 if (!(n < argc-1))
249                     error("Missing file name for the -b option.");
250                 moc.includeFiles.prepend(argv[++n]);
251             } else if (opt[1]) {
252                 moc.includeFiles.prepend(opt.mid(1));
253             }
254             break;
255         case 'p': // include file path
256             if (ignoreConflictingOptions)
257                 break;
258             if (!more) {
259                 if (!(n < argc-1))
260                     error("Missing path name for the -p option.");
261                 moc.includePath = argv[++n];
262             } else {
263                 moc.includePath = opt.mid(1);
264             }
265             break;
266         case 'I': // produce #include statement
267             if (!more) {
268                 if (!(n < argc-1))
269                     error("Missing path name for the -I option.");
270                 pp.includes += Preprocessor::IncludePath(argv[++n]);
271             } else {
272                 pp.includes += Preprocessor::IncludePath(opt.mid(1));
273             }
274             break;
275         case 'F': // minimalistic framework support for the mac
276             if (!more) {
277                 if (!(n < argc-1))
278                     error("Missing path name for the -F option.");
279                 Preprocessor::IncludePath p(argv[++n]);
280                 p.isFrameworkPath = true;
281                 pp.includes += p;
282             } else {
283                 Preprocessor::IncludePath p(opt.mid(1));
284                 p.isFrameworkPath = true;
285                 pp.includes += p;
286             }
287             break;
288         case 'D': // define macro
289             {
290                 QByteArray name;
291                 QByteArray value("1");
292                 if (!more) {
293                     if (n < argc-1)
294                         name = argv[++n];
295                 } else
296                     name = opt.mid(1);
297                 int eq = name.indexOf('=');
298                 if (eq >= 0) {
299                     value = name.mid(eq + 1);
300                     name = name.left(eq);
301                 }
302                 if (name.isEmpty())
303                     error("Missing macro name");
304                 Macro macro;
305                 macro.symbols += Symbol(0, PP_IDENTIFIER, value);
306                 pp.macros.insert(name, macro);
307
308             }
309             break;
310         case 'U':
311             {
312                 QByteArray macro;
313                 if (!more) {
314                     if (n < argc-1)
315                         macro = argv[++n];
316                 } else
317                     macro = opt.mid(1);
318                 if (macro.isEmpty())
319                     error("Missing macro name");
320                 pp.macros.remove(macro);
321
322             }
323             break;
324         case 'v':  // version number
325             if (more && opt != "version")
326                 error();
327             fprintf(stderr, "Qt Meta Object Compiler version %d (Qt %s)\n",
328                     mocOutputRevision, QT_VERSION_STR);
329             return 1;
330         case 'n': // don't display warnings
331             if (ignoreConflictingOptions)
332                 break;
333             if (opt == "nw")
334                 moc.displayWarnings = moc.displayNotes = false;
335             else if (opt == "nn")
336                 moc.displayNotes = false;
337             else
338                 error();
339             break;
340         case 'h': // help
341             if (more && opt != "help")
342                 error();
343             else
344                 error(0); // 0 means usage only
345             break;
346         case '-':
347             if (more && arg == "--ignore-option-clashes") {
348                 // -- ignore all following moc specific options that conflict
349                 // with for example gcc, like -pthread conflicting with moc's
350                 // -p option.
351                 ignoreConflictingOptions = true;
352                 break;
353             }
354             // fall through
355         default:
356             error();
357         }
358     }
359
360
361     if (autoInclude) {
362         int spos = filename.lastIndexOf(QDir::separator().toLatin1());
363         int ppos = filename.lastIndexOf('.');
364         // spos >= -1 && ppos > spos => ppos >= 0
365         moc.noInclude = (ppos > spos && tolower(filename[ppos + 1]) != 'h');
366     }
367     if (defaultInclude) {
368         if (moc.includePath.isEmpty()) {
369             if (filename.size()) {
370                 if (output.size())
371                     moc.includeFiles.append(combinePath(filename, output));
372                 else
373                     moc.includeFiles.append(filename);
374             }
375         } else {
376             moc.includeFiles.append(combinePath(filename, filename));
377         }
378     }
379
380     if (filename.isEmpty()) {
381         filename = "standard input";
382         in = stdin;
383     } else {
384 #if defined(_MSC_VER) && _MSC_VER >= 1400
385                 if (fopen_s(&in, filename.data(), "rb")) {
386 #else
387         in = fopen(filename.data(), "rb");
388                 if (!in) {
389 #endif
390             fprintf(stderr, "moc: %s: No such file\n", filename.constData());
391             return 1;
392         }
393         moc.filename = filename;
394     }
395
396     moc.currentFilenames.push(filename);
397
398     // 1. preprocess
399     moc.symbols = pp.preprocessed(moc.filename, in);
400     fclose(in);
401
402     if (!pp.preprocessOnly) {
403         // 2. parse
404         moc.parse();
405     }
406
407     // 3. and output meta object code
408
409     if (output.size()) { // output file specified
410 #if defined(_MSC_VER) && _MSC_VER >= 1400
411         if (fopen_s(&out, output.data(), "w"))
412 #else
413         out = fopen(output.data(), "w"); // create output file
414         if (!out)
415 #endif
416         {
417             fprintf(stderr, "moc: Cannot create %s\n", output.constData());
418             return 1;
419         }
420     } else { // use stdout
421         out = stdout;
422     }
423
424     if (pp.preprocessOnly) {
425         fprintf(out, "%s\n", composePreprocessorOutput(moc.symbols).constData());
426     } else {
427         if (moc.classList.isEmpty())
428             moc.note("No relevant classes found. No output generated.");
429         else
430             moc.generate(out);
431     }
432
433     if (output.size())
434         fclose(out);
435
436     return 0;
437 }
438
439 QT_END_NAMESPACE
440
441 int main(int _argc, char **_argv)
442 {
443     return QT_PREPEND_NAMESPACE(runMoc)(_argc, _argv);
444 }