Merge remote-tracking branch 'gerrit/master' into containers
[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 "../../corelib/global/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     return QFile::encodeName(outFileInfo.dir().relativeFilePath(inFileInfo.filePath()));
71 }
72
73
74 void error(const char *msg = "Invalid argument")
75 {
76     if (msg)
77         fprintf(stderr, "moc: %s\n", msg);
78     fprintf(stderr, "Usage: moc [options] <header-file>\n"
79             "  -o<file>           write output to file rather than stdout\n"
80             "  -I<dir>            add dir to the include path for header files\n"
81             "  -E                 preprocess only; do not generate meta object code\n"
82             "  -D<macro>[=<def>]  define macro, with optional definition\n"
83             "  -U<macro>          undefine macro\n"
84             "  -i                 do not generate an #include statement\n"
85             "  -p<path>           path prefix for included file\n"
86             "  -f[<file>]         force #include, optional file name (overwrite default)\n"
87             "  -b<file>           prepend #include <file> (preserve default include)\n"
88             "  -nn                do not display notes\n"
89             "  -nw                do not display warnings\n"
90             "  @<file>            read additional options from file\n"
91             "  -v                 display version of moc\n");
92     exit(1);
93 }
94
95
96 static inline bool hasNext(const Symbols &symbols, int i)
97 { return (i < symbols.size()); }
98
99 static inline const Symbol &next(const Symbols &symbols, int &i)
100 { return symbols.at(i++); }
101
102
103 QByteArray composePreprocessorOutput(const Symbols &symbols) {
104     QByteArray output;
105     int lineNum = 1;
106     Token last = PP_NOTOKEN;
107     Token secondlast = last;
108     int i = 0;
109     while (hasNext(symbols, i)) {
110         Symbol sym = next(symbols, i);
111         switch (sym.token) {
112         case PP_NEWLINE:
113         case PP_WHITESPACE:
114             if (last != PP_WHITESPACE) {
115                 secondlast = last;
116                 last = PP_WHITESPACE;
117                 output += ' ';
118             }
119             continue;
120         case PP_STRING_LITERAL:
121             if (last == PP_STRING_LITERAL)
122                 output.chop(1);
123             else if (secondlast == PP_STRING_LITERAL && last == PP_WHITESPACE)
124                 output.chop(2);
125             else
126                 break;
127             output += sym.lexem().mid(1);
128             secondlast = last;
129             last = PP_STRING_LITERAL;
130             continue;
131         case MOC_INCLUDE_BEGIN:
132             lineNum = 0;
133             continue;
134         case MOC_INCLUDE_END:
135             lineNum = sym.lineNum;
136             continue;
137         default:
138             break;
139         }
140         secondlast = last;
141         last = sym.token;
142
143         const int padding = sym.lineNum - lineNum;
144         if (padding > 0) {
145             output.resize(output.size() + padding);
146             qMemSet(output.data() + output.size() - padding, '\n', padding);
147             lineNum = sym.lineNum;
148         }
149
150         output += sym.lexem();
151     }
152
153     return output;
154 }
155
156
157 int runMoc(int _argc, char **_argv)
158 {
159     bool autoInclude = true;
160     bool defaultInclude = true;
161     Preprocessor pp;
162     Moc moc;
163     pp.macros["Q_MOC_RUN"];
164     pp.macros["__cplusplus"];
165     QByteArray filename;
166     QByteArray output;
167     FILE *in = 0;
168     FILE *out = 0;
169     bool ignoreConflictingOptions = false;
170
171     QVector<QByteArray> argv;
172     argv.resize(_argc - 1);
173     for (int n = 1; n < _argc; ++n)
174         argv[n - 1] = _argv[n];
175     int argc = argv.count();
176
177     for (int n = 0; n < argv.count(); ++n) {
178         if (argv.at(n).startsWith('@')) {
179             QByteArray optionsFile = argv.at(n);
180             optionsFile.remove(0, 1);
181             if (optionsFile.isEmpty())
182                 error("The @ option requires an input file");
183             QFile f(QString::fromLatin1(optionsFile.constData()));
184             if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
185                 error("Cannot open options file specified with @");
186             argv.remove(n);
187             while (!f.atEnd()) {
188                 QByteArray line = f.readLine().trimmed();
189                 if (!line.isEmpty())
190                     argv.insert(n++, line);
191             }
192         }
193     }
194
195     argc = argv.count();
196
197     for (int n = 0; n < argc; ++n) {
198         QByteArray arg(argv[n]);
199         if (arg[0] != '-') {
200             if (filename.isEmpty()) {
201                 filename = arg;
202                 continue;
203             }
204             error("Too many input files specified");
205         }
206         QByteArray opt = arg.mid(1);
207         bool more = (opt.size() > 1);
208         switch (opt[0]) {
209         case 'o': // output redirection
210             if (!more) {
211                 if (!(n < argc-1))
212                     error("Missing output file name");
213                 output = argv[++n];
214             } else
215                 output = opt.mid(1);
216             break;
217         case 'E': // only preprocessor
218             pp.preprocessOnly = true;
219             break;
220         case 'i': // no #include statement
221             if (more)
222                 error();
223             moc.noInclude        = true;
224             autoInclude = false;
225             break;
226         case 'f': // produce #include statement
227             if (ignoreConflictingOptions)
228                 break;
229             moc.noInclude        = false;
230             autoInclude = false;
231             if (opt[1]) {                       // -fsomething.h
232                 moc.includeFiles.append(opt.mid(1));
233                 defaultInclude = false;
234             }
235             break;
236         case 'b':
237             if (ignoreConflictingOptions)
238                 break;
239             if (!more) {
240                 if (!(n < argc-1))
241                     error("Missing file name for the -b option.");
242                 moc.includeFiles.prepend(argv[++n]);
243             } else if (opt[1]) {
244                 moc.includeFiles.prepend(opt.mid(1));
245             }
246             break;
247         case 'p': // include file path
248             if (ignoreConflictingOptions)
249                 break;
250             if (!more) {
251                 if (!(n < argc-1))
252                     error("Missing path name for the -p option.");
253                 moc.includePath = argv[++n];
254             } else {
255                 moc.includePath = opt.mid(1);
256             }
257             break;
258         case 'I': // produce #include statement
259             if (!more) {
260                 if (!(n < argc-1))
261                     error("Missing path name for the -I option.");
262                 pp.includes += Preprocessor::IncludePath(argv[++n]);
263             } else {
264                 pp.includes += Preprocessor::IncludePath(opt.mid(1));
265             }
266             break;
267         case 'F': // minimalistic framework support for the mac
268             if (!more) {
269                 if (!(n < argc-1))
270                     error("Missing path name for the -F option.");
271                 Preprocessor::IncludePath p(argv[++n]);
272                 p.isFrameworkPath = true;
273                 pp.includes += p;
274             } else {
275                 Preprocessor::IncludePath p(opt.mid(1));
276                 p.isFrameworkPath = true;
277                 pp.includes += p;
278             }
279             break;
280         case 'D': // define macro
281             {
282                 QByteArray name;
283                 QByteArray value("1");
284                 if (!more) {
285                     if (n < argc-1)
286                         name = argv[++n];
287                 } else
288                     name = opt.mid(1);
289                 int eq = name.indexOf('=');
290                 if (eq >= 0) {
291                     value = name.mid(eq + 1);
292                     name = name.left(eq);
293                 }
294                 if (name.isEmpty())
295                     error("Missing macro name");
296                 Macro macro;
297                 macro.symbols += Symbol(0, PP_IDENTIFIER, value);
298                 pp.macros.insert(name, macro);
299
300             }
301             break;
302         case 'U':
303             {
304                 QByteArray macro;
305                 if (!more) {
306                     if (n < argc-1)
307                         macro = argv[++n];
308                 } else
309                     macro = opt.mid(1);
310                 if (macro.isEmpty())
311                     error("Missing macro name");
312                 pp.macros.remove(macro);
313
314             }
315             break;
316         case 'v':  // version number
317             if (more && opt != "version")
318                 error();
319             fprintf(stderr, "Qt Meta Object Compiler version %d (Qt %s)\n",
320                     mocOutputRevision, QT_VERSION_STR);
321             return 1;
322         case 'n': // don't display warnings
323             if (ignoreConflictingOptions)
324                 break;
325             if (opt == "nw")
326                 moc.displayWarnings = moc.displayNotes = false;
327             else if (opt == "nn")
328                 moc.displayNotes = false;
329             else
330                 error();
331             break;
332         case 'h': // help
333             if (more && opt != "help")
334                 error();
335             else
336                 error(0); // 0 means usage only
337             break;
338         case '-':
339             if (more && arg == "--ignore-option-clashes") {
340                 // -- ignore all following moc specific options that conflict
341                 // with for example gcc, like -pthread conflicting with moc's
342                 // -p option.
343                 ignoreConflictingOptions = true;
344                 break;
345             }
346             // fall through
347         default:
348             error();
349         }
350     }
351
352
353     if (autoInclude) {
354         int spos = filename.lastIndexOf(QDir::separator().toLatin1());
355         int ppos = filename.lastIndexOf('.');
356         // spos >= -1 && ppos > spos => ppos >= 0
357         moc.noInclude = (ppos > spos && tolower(filename[ppos + 1]) != 'h');
358     }
359     if (defaultInclude) {
360         if (moc.includePath.isEmpty()) {
361             if (filename.size()) {
362                 if (output.size())
363                     moc.includeFiles.append(combinePath(filename, output));
364                 else
365                     moc.includeFiles.append(filename);
366             }
367         } else {
368             moc.includeFiles.append(combinePath(filename, filename));
369         }
370     }
371
372     if (filename.isEmpty()) {
373         filename = "standard input";
374         in = stdin;
375     } else {
376 #if defined(_MSC_VER) && _MSC_VER >= 1400
377                 if (fopen_s(&in, filename.data(), "rb")) {
378 #else
379         in = fopen(filename.data(), "rb");
380                 if (!in) {
381 #endif
382             fprintf(stderr, "moc: %s: No such file\n", filename.constData());
383             return 1;
384         }
385         moc.filename = filename;
386     }
387
388     moc.currentFilenames.push(filename);
389
390     // 1. preprocess
391     moc.symbols = pp.preprocessed(moc.filename, in);
392     fclose(in);
393
394     if (!pp.preprocessOnly) {
395         // 2. parse
396         moc.parse();
397     }
398
399     // 3. and output meta object code
400
401     if (output.size()) { // output file specified
402 #if defined(_MSC_VER) && _MSC_VER >= 1400
403         if (fopen_s(&out, output.data(), "w"))
404 #else
405         out = fopen(output.data(), "w"); // create output file
406         if (!out)
407 #endif
408         {
409             fprintf(stderr, "moc: Cannot create %s\n", output.constData());
410             return 1;
411         }
412     } else { // use stdout
413         out = stdout;
414     }
415
416     if (pp.preprocessOnly) {
417         fprintf(out, "%s\n", composePreprocessorOutput(moc.symbols).constData());
418     } else {
419         if (moc.classList.isEmpty())
420             moc.note("No relevant classes found. No output generated.");
421         else
422             moc.generate(out);
423     }
424
425     if (output.size())
426         fclose(out);
427
428     return 0;
429 }
430
431 QT_END_NAMESPACE
432
433 int main(int _argc, char **_argv)
434 {
435     return QT_PREPEND_NAMESPACE(runMoc)(_argc, _argv);
436 }