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

Last change on this file since 5 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 14.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information (qt-info@nokia.com)
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department at qt-sales@nokia.com.
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 int argc = argv.count();
200
201 for (int n = 0; n < argv.count(); ++n) {
202 if (argv.at(n).startsWith('@')) {
203 QByteArray optionsFile = argv.at(n);
204 optionsFile.remove(0, 1);
205 if (optionsFile.isEmpty())
206 error("The @ option requires an input file");
207 QFile f(QString::fromLatin1(optionsFile.constData()));
208 if (!f.open(QIODevice::ReadOnly | QIODevice::Text))
209 error("Cannot open options file specified with @");
210 argv.remove(n);
211 while (!f.atEnd()) {
212 QByteArray line = f.readLine().trimmed();
213 if (!line.isEmpty())
214 argv.insert(n++, line);
215 }
216 }
217 }
218
219 // report Qt usage for commercial customers with a "metered license" (currently experimental)
220#if QT_EDITION != QT_EDITION_OPENSOURCE
221#ifdef QT_CONFIGURE_BINARIES_PATH
222 const char *binariesPath = QT_CONFIGURE_BINARIES_PATH;
223 QString reporterPath = QString::fromLocal8Bit(binariesPath) + QDir::separator()
224 + QLatin1String("qtusagereporter");
225#if defined(Q_OS_WIN)
226 reporterPath += QLatin1String(".exe");
227#endif
228 if (QFile::exists(reporterPath))
229 system(qPrintable(reporterPath + QLatin1String(" moc")));
230#endif
231#endif
232
233 argc = argv.count();
234
235 for (int n = 0; n < argc; ++n) {
236 QByteArray arg(argv[n]);
237 if (arg[0] != '-') {
238 if (filename.isEmpty()) {
239 filename = arg;
240 continue;
241 }
242 error("Too many input files specified");
243 }
244 QByteArray opt = arg.mid(1);
245 bool more = (opt.size() > 1);
246 switch (opt[0]) {
247 case 'o': // output redirection
248 if (!more) {
249 if (!(n < argc-1))
250 error("Missing output file name");
251 output = argv[++n];
252 } else
253 output = opt.mid(1);
254 break;
255 case 'E': // only preprocessor
256 pp.preprocessOnly = true;
257 break;
258 case 'i': // no #include statement
259 if (more)
260 error();
261 moc.noInclude = true;
262 autoInclude = false;
263 break;
264 case 'f': // produce #include statement
265 if (ignoreConflictingOptions)
266 break;
267 moc.noInclude = false;
268 autoInclude = false;
269 if (opt[1]) // -fsomething.h
270 moc.includeFiles.append(opt.mid(1));
271 break;
272 case 'p': // include file path
273 if (ignoreConflictingOptions)
274 break;
275 if (!more) {
276 if (!(n < argc-1))
277 error("Missing path name for the -p option.");
278 moc.includePath = argv[++n];
279 } else {
280 moc.includePath = opt.mid(1);
281 }
282 break;
283 case 'I': // produce #include statement
284 if (!more) {
285 if (!(n < argc-1))
286 error("Missing path name for the -I option.");
287 pp.includes += Preprocessor::IncludePath(argv[++n]);
288 } else {
289 pp.includes += Preprocessor::IncludePath(opt.mid(1));
290 }
291 break;
292 case 'F': // minimalistic framework support for the mac
293 if (!more) {
294 if (!(n < argc-1))
295 error("Missing path name for the -F option.");
296 Preprocessor::IncludePath p(argv[++n]);
297 p.isFrameworkPath = true;
298 pp.includes += p;
299 } else {
300 Preprocessor::IncludePath p(opt.mid(1));
301 p.isFrameworkPath = true;
302 pp.includes += p;
303 }
304 break;
305 case 'D': // define macro
306 {
307 QByteArray name;
308 QByteArray value("1");
309 if (!more) {
310 if (n < argc-1)
311 name = argv[++n];
312 } else
313 name = opt.mid(1);
314 int eq = name.indexOf('=');
315 if (eq >= 0) {
316 value = name.mid(eq + 1);
317 name = name.left(eq);
318 }
319 if (name.isEmpty())
320 error("Missing macro name");
321 Macro macro;
322 macro.symbols += Symbol(0, PP_IDENTIFIER, value);
323 pp.macros.insert(name, macro);
324
325 }
326 break;
327 case 'U':
328 {
329 QByteArray macro;
330 if (!more) {
331 if (n < argc-1)
332 macro = argv[++n];
333 } else
334 macro = opt.mid(1);
335 if (macro.isEmpty())
336 error("Missing macro name");
337 pp.macros.remove(macro);
338
339 }
340 break;
341 case 'v': // version number
342 if (more && opt != "version")
343 error();
344 fprintf(stderr, "Qt Meta Object Compiler version %d (Qt %s)\n",
345 mocOutputRevision, QT_VERSION_STR);
346 return 1;
347 case 'n': // don't display warnings
348 if (ignoreConflictingOptions)
349 break;
350 if (opt != "nw")
351 error();
352 moc.displayWarnings = false;
353 break;
354 case 'h': // help
355 if (more && opt != "help")
356 error();
357 else
358 error(0); // 0 means usage only
359 break;
360 case '-':
361 if (more && arg == "--ignore-option-clashes") {
362 // -- ignore all following moc specific options that conflict
363 // with for example gcc, like -pthread conflicting with moc's
364 // -p option.
365 ignoreConflictingOptions = true;
366 break;
367 }
368 // fall through
369 default:
370 error();
371 }
372 }
373
374
375 if (autoInclude) {
376 int ppos = filename.lastIndexOf('.');
377 moc.noInclude = (ppos >= 0
378 && tolower(filename[ppos + 1]) != 'h'
379 && tolower(filename[ppos + 1]) != QDir::separator().toLatin1()
380 );
381 }
382 if (moc.includeFiles.isEmpty()) {
383 if (moc.includePath.isEmpty()) {
384 if (filename.size()) {
385 if (output.size())
386 moc.includeFiles.append(combinePath(filename, output));
387 else
388 moc.includeFiles.append(filename);
389 }
390 } else {
391 moc.includeFiles.append(combinePath(filename, filename));
392 }
393 }
394
395 if (filename.isEmpty()) {
396 filename = "standard input";
397 in = stdin;
398 } else {
399#if defined(_MSC_VER) && _MSC_VER >= 1400
400 if (fopen_s(&in, filename.data(), "rb")) {
401#else
402 in = fopen(filename.data(), "rb");
403 if (!in) {
404#endif
405 fprintf(stderr, "moc: %s: No such file\n", (const char*)filename);
406 return 1;
407 }
408 moc.filename = filename;
409 }
410
411 moc.currentFilenames.push(filename);
412
413 // 1. preprocess
414 moc.symbols = pp.preprocessed(moc.filename, in);
415 fclose(in);
416
417 if (!pp.preprocessOnly) {
418 // 2. parse
419 moc.parse();
420 }
421
422 // 3. and output meta object code
423
424 if (output.size()) { // output file specified
425#if defined(_MSC_VER) && _MSC_VER >= 1400
426 if (fopen_s(&out, output.data(), "w"))
427#else
428 out = fopen(output.data(), "w"); // create output file
429 if (!out)
430#endif
431 {
432 fprintf(stderr, "moc: Cannot create %s\n", (const char*)output);
433 return 1;
434 }
435 } else { // use stdout
436 out = stdout;
437 }
438
439 if (pp.preprocessOnly) {
440 fprintf(out, "%s\n", composePreprocessorOutput(moc.symbols).constData());
441 } else {
442 if (moc.classList.isEmpty())
443 moc.warning("No relevant classes found. No output generated.");
444 else
445 moc.generate(out);
446 }
447
448 if (output.size())
449 fclose(out);
450
451 return 0;
452}
453
454QT_END_NAMESPACE
455
456int main(int _argc, char **_argv)
457{
458 return QT_PREPEND_NAMESPACE(runMoc)(_argc, _argv);
459}
Note: See TracBrowser for help on using the repository browser.