/* Copyright (C) 2009 Arno Rehn This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "generatorpreprocessor.h" #include "generatorvisitor.h" #include "options.h" typedef int (*GenerateFn)(); static void showUsage() { std::cout << "Usage: smokegen [options] --
" << std::endl << "Possible command line options are:" << std::endl << " -I " << std::endl << " -d " << std::endl << " -dm " << std::endl << " -g " << std::endl << " -qt enables Qt-mode (special treatment of QFlags)" << std::endl << " -t resolve typedefs" << std::endl << " -o " << std::endl << " -config " << std::endl << " -h shows this message" << std::endl; } int main(int argc, char **argv) { if (argc == 1) { showUsage(); return EXIT_SUCCESS; } QCoreApplication app(argc, argv); const QStringList& args = app.arguments(); QFileInfo configFile; QString generator; bool addHeaders = false; bool hasCommandLineGenerator = false; QStringList classes; for (int i = 1; i < args.count(); i++) { if ((args[i] == "-I" || args[i] == "-d" || args[i] == "-dm" || args[i] == "-g" || args[i] == "-config") && i + 1 >= args.count()) { qCritical() << "not enough parameters for option" << args[i]; return EXIT_FAILURE; } if (args[i] == "-I") { ParserOptions::includeDirs << QDir(args[++i]); } else if (args[i] == "-config") { configFile = QFileInfo(args[++i]); } else if (args[i] == "-d") { ParserOptions::definesList = QFileInfo(args[++i]); } else if (args[i] == "-dm") { ParserOptions::dropMacros += args[++i].split(','); } else if (args[i] == "-g") { generator = args[++i]; hasCommandLineGenerator = true; } else if ((args[i] == "-h" || args[i] == "--help") && argc == 2) { showUsage(); return EXIT_SUCCESS; } else if (args[i] == "-t") { ParserOptions::resolveTypedefs = true; } else if (args[i] == "-qt") { ParserOptions::qtMode = true; } else if (args[i] == "--") { addHeaders = true; } else if (addHeaders) { ParserOptions::headerList << QFileInfo(args[i]); } } if (configFile.exists()) { QFile file(configFile.filePath()); file.open(QIODevice::ReadOnly); QDomDocument doc; doc.setContent(file.readAll()); file.close(); QDomElement root = doc.documentElement(); QDomNode node = root.firstChild(); while (!node.isNull()) { QDomElement elem = node.toElement(); if (elem.isNull()) { node = node.nextSibling(); continue; } if (elem.tagName() == "resolveTypedefs") { ParserOptions::resolveTypedefs = (elem.text() == "true"); } else if (elem.tagName() == "qtMode") { ParserOptions::qtMode = (elem.text() == "true"); } else if (!hasCommandLineGenerator && elem.tagName() == "generator") { generator = elem.text(); } else if (elem.tagName() == "includeDirs") { QDomNode dir = elem.firstChild(); while (!dir.isNull()) { QDomElement elem = dir.toElement(); if (elem.isNull()) { dir = dir.nextSibling(); continue; } if (elem.tagName() == "dir") { ParserOptions::includeDirs << QDir(elem.text()); } dir = dir.nextSibling(); } } else if (elem.tagName() == "definesList") { // reference to an external file, so it can be auto-generated ParserOptions::definesList = QFileInfo(elem.text()); } else if (elem.tagName() == "dropMacros") { QDomNode macro = elem.firstChild(); while (!macro.isNull()) { QDomElement elem = macro.toElement(); if (elem.isNull()) { macro = macro.nextSibling(); continue; } if (elem.tagName() == "name") { ParserOptions::dropMacros << elem.text(); } macro = macro.nextSibling(); } } node = node.nextSibling(); } } else { qWarning() << "Couldn't find config file" << configFile.filePath(); } // first try to load plugins from the executable's directory QLibrary lib(app.applicationDirPath() + "/generator_" + generator); lib.load(); if (!lib.isLoaded()) { lib.unload(); lib.setFileName(app.applicationDirPath() + "/../lib/smokegen/generator_" + generator); lib.load(); } if (!lib.isLoaded()) { lib.unload(); lib.setFileName("generator_" + generator); lib.load(); } if (!lib.isLoaded()) { qCritical() << lib.errorString(); return EXIT_FAILURE; } qDebug() << "using generator" << lib.fileName(); GenerateFn generate = (GenerateFn) lib.resolve("generate"); if (!generate) { qCritical() << "couldn't resolve symbol 'generate', aborting"; return EXIT_FAILURE; } foreach (QDir dir, ParserOptions::includeDirs) { if (!dir.exists()) { qWarning() << "include directory" << dir.path() << "doesn't exist"; ParserOptions::includeDirs.removeAll(dir); } } QStringList defines; if (ParserOptions::definesList.exists()) { QFile file(ParserOptions::definesList.filePath()); file.open(QIODevice::ReadOnly); while (!file.atEnd()) { QByteArray array = file.readLine(); if (!array.isEmpty()) defines << array.trimmed(); } file.close(); } else if (!ParserOptions::definesList.filePath().isEmpty()) { qWarning() << "didn't find file" << ParserOptions::definesList.filePath(); } QFile log("generator.log"); bool logErrors = log.open(QFile::WriteOnly | QFile::Truncate); QTextStream logOut(&log); foreach (QFileInfo file, ParserOptions::headerList) { qDebug() << "parsing" << file.absoluteFilePath(); // this has already been parsed because it was included by some header if (parsedHeaders.contains(file.absoluteFilePath())) continue; Preprocessor pp(ParserOptions::includeDirs, defines, file); Control c; Parser parser(&c); ParseSession session; session.setContentsAndGenerateLocationTable(pp.preprocess()); TranslationUnitAST* ast = parser.parse(&session); // TODO: improve 'header => class' association GeneratorVisitor visitor(&session, file.fileName()); visitor.visit(ast); if (!logErrors) continue; foreach (const Problem* p, c.problems()) { logOut << file.fileName() << ": " << p->file << "(" << p->position.line << ", " << p->position.column << "): " << p->description << "\n"; } } log.close(); return generate(); }