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

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

moc: Fixed: If a @<file> command line option immediately followed another @<file> option, it was treaten as an input file name [vendor bug].

File size: 14.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
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 [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 // report Qt usage for commercial customers with a "metered license" (currently experimental)
222#if QT_EDITION != QT_EDITION_OPENSOURCE
223#ifdef QT_CONFIGURE_BINARIES_PATH
224 const char *binariesPath = QT_CONFIGURE_BINARIES_PATH;
225 QString reporterPath = QString::fromLocal8Bit(binariesPath) + QDir::separator()
226 + QLatin1String("qtusagereporter");
227#if defined(Q_OS_WIN)
228 reporterPath += QLatin1String(".exe");
229#endif
230 if (QFile::exists(reporterPath))
231 system(qPrintable(reporterPath + QLatin1String(" moc")));
232#endif
233#endif
234
235 int argc = argv.count();
236
237 for (int n = 0; n < argc; ++n) {
238 QByteArray arg(argv[n]);
239 if (arg[0] != '-') {
240 if (filename.isEmpty()) {
241 filename = arg;
242 continue;
243 }
244 error("Too many input files specified");
245 }
246 QByteArray opt = arg.mid(1);
247 bool more = (opt.size() > 1);
248 switch (opt[0]) {
249 case 'o': // output redirection
250 if (!more) {
251 if (!(n < argc-1))
252 error("Missing output file name");
253 output = argv[++n];
254 } else
255 output = opt.mid(1);
256 break;
257 case 'E': // only preprocessor
258 pp.preprocessOnly = true;
259 break;
260 case 'i': // no #include statement
261 if (more)
262 error();
263 moc.noInclude = true;
264 autoInclude = false;
265 break;
266 case 'f': // produce #include statement
267 if (ignoreConflictingOptions)
268 break;
269 moc.noInclude = false;
270 autoInclude = false;
271 if (opt[1]) // -fsomething.h
272 moc.includeFiles.append(opt.mid(1));
273 break;
274 case 'p': // include file path
275 if (ignoreConflictingOptions)
276 break;
277 if (!more) {
278 if (!(n < argc-1))
279 error("Missing path name for the -p option.");
280 moc.includePath = argv[++n];
281 } else {
282 moc.includePath = opt.mid(1);
283 }
284 break;
285 case 'I': // produce #include statement
286 if (!more) {
287 if (!(n < argc-1))
288 error("Missing path name for the -I option.");
289 pp.includes += Preprocessor::IncludePath(argv[++n]);
290 } else {
291 pp.includes += Preprocessor::IncludePath(opt.mid(1));
292 }
293 break;
294 case 'F': // minimalistic framework support for the mac
295 if (!more) {
296 if (!(n < argc-1))
297 error("Missing path name for the -F option.");
298 Preprocessor::IncludePath p(argv[++n]);
299 p.isFrameworkPath = true;
300 pp.includes += p;
301 } else {
302 Preprocessor::IncludePath p(opt.mid(1));
303 p.isFrameworkPath = true;
304 pp.includes += p;
305 }
306 break;
307 case 'D': // define macro
308 {
309 QByteArray name;
310 QByteArray value("1");
311 if (!more) {
312 if (n < argc-1)
313 name = argv[++n];
314 } else
315 name = opt.mid(1);
316 int eq = name.indexOf('=');
317 if (eq >= 0) {
318 value = name.mid(eq + 1);
319 name = name.left(eq);
320 }
321 if (name.isEmpty())
322 error("Missing macro name");
323 Macro macro;
324 macro.symbols += Symbol(0, PP_IDENTIFIER, value);
325 pp.macros.insert(name, macro);
326
327 }
328 break;
329 case 'U':
330 {
331 QByteArray macro;
332 if (!more) {
333 if (n < argc-1)
334 macro = argv[++n];
335 } else
336 macro = opt.mid(1);
337 if (macro.isEmpty())
338 error("Missing macro name");
339 pp.macros.remove(macro);
340
341 }
342 break;
343 case 'v': // version number
344 if (more && opt != "version")
345 error();
346 fprintf(stderr, "Qt Meta Object Compiler version %d (Qt %s)\n",
347 mocOutputRevision, QT_VERSION_STR);
348 return 1;
349 case 'n': // don't display warnings
350 if (ignoreConflictingOptions)
351 break;
352 if (opt != "nw")
353 error();
354 moc.displayWarnings = false;
355 break;
356 case 'h': // help
357 if (more && opt != "help")
358 error();
359 else
360 error(0); // 0 means usage only
361 break;
362 case '-':
363 if (more && arg == "--ignore-option-clashes") {
364 // -- ignore all following moc specific options that conflict
365 // with for example gcc, like -pthread conflicting with moc's
366 // -p option.
367 ignoreConflictingOptions = true;
368 break;
369 }
370 // fall through
371 default:
372 error();
373 }
374 }
375
376
377 if (autoInclude) {
378 int ppos = filename.lastIndexOf('.');
379 moc.noInclude = (ppos >= 0
380 && tolower(filename[ppos + 1]) != 'h'
381 && tolower(filename[ppos + 1]) != QDir::separator().toLatin1()
382 );
383 }
384 if (moc.includeFiles.isEmpty()) {
385 if (moc.includePath.isEmpty()) {
386 if (filename.size()) {
387 if (output.size())
388 moc.includeFiles.append(combinePath(filename, output));
389 else
390 moc.includeFiles.append(filename);
391 }
392 } else {
393 moc.includeFiles.append(combinePath(filename, filename));
394 }
395 }
396
397 if (filename.isEmpty()) {
398 filename = "standard input";
399 in = stdin;
400 } else {
401#if defined(_MSC_VER) && _MSC_VER >= 1400
402 if (fopen_s(&in, filename.data(), "rb")) {
403#else
404 in = fopen(filename.data(), "rb");
405 if (!in) {
406#endif
407 fprintf(stderr, "moc: %s: No such file\n", (const char*)filename);
408 return 1;
409 }
410 moc.filename = filename;
411 }
412
413 moc.currentFilenames.push(filename);
414
415 // 1. preprocess
416 moc.symbols = pp.preprocessed(moc.filename, in);
417 fclose(in);
418
419 if (!pp.preprocessOnly) {
420 // 2. parse
421 moc.parse();
422 }
423
424 // 3. and output meta object code
425
426 if (output.size()) { // output file specified
427#if defined(_MSC_VER) && _MSC_VER >= 1400
428 if (fopen_s(&out, output.data(), "w"))
429#else
430 out = fopen(output.data(), "w"); // create output file
431 if (!out)
432#endif
433 {
434 fprintf(stderr, "moc: Cannot create %s\n", (const char*)output);
435 return 1;
436 }
437 } else { // use stdout
438 out = stdout;
439 }
440
441 if (pp.preprocessOnly) {
442 fprintf(out, "%s\n", composePreprocessorOutput(moc.symbols).constData());
443 } else {
444 if (moc.classList.isEmpty())
445 moc.warning("No relevant classes found. No output generated.");
446 else
447 moc.generate(out);
448 }
449
450 if (output.size())
451 fclose(out);
452
453 return 0;
454}
455
456QT_END_NAMESPACE
457
458int main(int _argc, char **_argv)
459{
460 return QT_PREPEND_NAMESPACE(runMoc)(_argc, _argv);
461}
Note: See TracBrowser for help on using the repository browser.