source: trunk/src/tools/moc/main.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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