1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the tools applications of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "preprocessor.h"
44 #include "outputrevision.h"
45 #include <qconfig.cpp>
56 This function looks at two file names and returns the name of the
57 infile with a path relative to outfile.
61 /tmp/abc, /tmp/bcd -> abc
62 xyz/a/bc, xyz/b/ac -> ../a/bc
63 /tmp/abc, xyz/klm -> /tmp/abc
66 static QByteArray combinePath(const QByteArray &infile, const QByteArray &outfile)
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()));
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());
82 void error(const char *msg = "Invalid argument")
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");
104 static inline bool hasNext(const Symbols &symbols, int i)
105 { return (i < symbols.size()); }
107 static inline const Symbol &next(const Symbols &symbols, int &i)
108 { return symbols.at(i++); }
111 QByteArray composePreprocessorOutput(const Symbols &symbols) {
114 Token last = PP_NOTOKEN;
115 Token secondlast = last;
117 while (hasNext(symbols, i)) {
118 Symbol sym = next(symbols, i);
122 if (last != PP_WHITESPACE) {
124 last = PP_WHITESPACE;
128 case PP_STRING_LITERAL:
129 if (last == PP_STRING_LITERAL)
131 else if (secondlast == PP_STRING_LITERAL && last == PP_WHITESPACE)
135 output += sym.lexem().mid(1);
137 last = PP_STRING_LITERAL;
139 case MOC_INCLUDE_BEGIN:
142 case MOC_INCLUDE_END:
143 lineNum = sym.lineNum;
151 const int padding = sym.lineNum - lineNum;
153 output.resize(output.size() + padding);
154 memset(output.data() + output.size() - padding, '\n', padding);
155 lineNum = sym.lineNum;
158 output += sym.lexem();
165 int runMoc(int _argc, char **_argv)
167 bool autoInclude = true;
168 bool defaultInclude = true;
171 pp.macros["Q_MOC_RUN"];
172 pp.macros["__cplusplus"];
177 bool ignoreConflictingOptions = false;
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();
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 @");
196 QByteArray line = f.readLine().trimmed();
198 argv.insert(n++, line);
205 for (int n = 0; n < argc; ++n) {
206 QByteArray arg(argv[n]);
208 if (filename.isEmpty()) {
212 error("Too many input files specified");
214 QByteArray opt = arg.mid(1);
215 bool more = (opt.size() > 1);
217 case 'o': // output redirection
220 error("Missing output file name");
225 case 'E': // only preprocessor
226 pp.preprocessOnly = true;
228 case 'i': // no #include statement
231 moc.noInclude = true;
234 case 'f': // produce #include statement
235 if (ignoreConflictingOptions)
237 moc.noInclude = false;
239 if (opt[1]) { // -fsomething.h
240 moc.includeFiles.append(opt.mid(1));
241 defaultInclude = false;
245 if (ignoreConflictingOptions)
249 error("Missing file name for the -b option.");
250 moc.includeFiles.prepend(argv[++n]);
252 moc.includeFiles.prepend(opt.mid(1));
255 case 'p': // include file path
256 if (ignoreConflictingOptions)
260 error("Missing path name for the -p option.");
261 moc.includePath = argv[++n];
263 moc.includePath = opt.mid(1);
266 case 'I': // produce #include statement
269 error("Missing path name for the -I option.");
270 pp.includes += Preprocessor::IncludePath(argv[++n]);
272 pp.includes += Preprocessor::IncludePath(opt.mid(1));
275 case 'F': // minimalistic framework support for the mac
278 error("Missing path name for the -F option.");
279 Preprocessor::IncludePath p(argv[++n]);
280 p.isFrameworkPath = true;
283 Preprocessor::IncludePath p(opt.mid(1));
284 p.isFrameworkPath = true;
288 case 'D': // define macro
291 QByteArray value("1");
297 int eq = name.indexOf('=');
299 value = name.mid(eq + 1);
300 name = name.left(eq);
303 error("Missing macro name");
305 macro.symbols += Symbol(0, PP_IDENTIFIER, value);
306 pp.macros.insert(name, macro);
319 error("Missing macro name");
320 pp.macros.remove(macro);
324 case 'v': // version number
325 if (more && opt != "version")
327 fprintf(stderr, "Qt Meta Object Compiler version %d (Qt %s)\n",
328 mocOutputRevision, QT_VERSION_STR);
330 case 'n': // don't display warnings
331 if (ignoreConflictingOptions)
334 moc.displayWarnings = moc.displayNotes = false;
335 else if (opt == "nn")
336 moc.displayNotes = false;
341 if (more && opt != "help")
344 error(0); // 0 means usage only
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
351 ignoreConflictingOptions = true;
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');
367 if (defaultInclude) {
368 if (moc.includePath.isEmpty()) {
369 if (filename.size()) {
371 moc.includeFiles.append(combinePath(filename, output));
373 moc.includeFiles.append(filename);
376 moc.includeFiles.append(combinePath(filename, filename));
380 if (filename.isEmpty()) {
381 filename = "standard input";
384 #if defined(_MSC_VER) && _MSC_VER >= 1400
385 if (fopen_s(&in, filename.data(), "rb")) {
387 in = fopen(filename.data(), "rb");
390 fprintf(stderr, "moc: %s: No such file\n", filename.constData());
393 moc.filename = filename;
396 moc.currentFilenames.push(filename);
399 moc.symbols = pp.preprocessed(moc.filename, in);
402 if (!pp.preprocessOnly) {
407 // 3. and output meta object code
409 if (output.size()) { // output file specified
410 #if defined(_MSC_VER) && _MSC_VER >= 1400
411 if (fopen_s(&out, output.data(), "w"))
413 out = fopen(output.data(), "w"); // create output file
417 fprintf(stderr, "moc: Cannot create %s\n", output.constData());
420 } else { // use stdout
424 if (pp.preprocessOnly) {
425 fprintf(out, "%s\n", composePreprocessorOutput(moc.symbols).constData());
427 if (moc.classList.isEmpty())
428 moc.note("No relevant classes found. No output generated.");
441 int main(int _argc, char **_argv)
443 return QT_PREPEND_NAMESPACE(runMoc)(_argc, _argv);