#include <QCoreApplication>
#include <QList>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QLibrary>
#include <QtXml>
#include <QtDebug>
#include <control.h>
#include <parsesession.h>
#include <parser.h>
#include <ast.h>
#include <iostream>
#include "generatorpreprocessor.h"
#include "generatorvisitor.h"
#include "options.h"
typedef
int
(*GenerateFn)();
static
void
showUsage()
{
std::cout <<
"Usage: smokegen [options] -- <header files>"
<< std::endl <<
"Possible command line options are:"
<< std::endl <<
" -I <include dir>"
<< std::endl <<
" -d <path to file containing #defines>"
<< std::endl <<
" -dm <list of macros that should be ignored>"
<< std::endl <<
" -g <generator to use>"
<< std::endl <<
" -qt enables Qt-mode (special treatment of QFlags)"
<< std::endl <<
" -t resolve typedefs"
<< std::endl <<
" -o <output dir>"
<< std::endl <<
" -config <config file>"
<< 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"
) {
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();
}
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();
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);
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();
}