diff options
Diffstat (limited to 'generator/main.cpp')
-rw-r--r-- | generator/main.cpp | 443 |
1 files changed, 443 insertions, 0 deletions
diff --git a/generator/main.cpp b/generator/main.cpp new file mode 100644 index 000000000..f9f94f7ac --- /dev/null +++ b/generator/main.cpp @@ -0,0 +1,443 @@ +/* + * This file is part of the PySide project. + * + * Copyright (C) 2009-2012 Nokia Corporation and/or its subsidiary(-ies). + * + * Contact: PySide team <contact@pyside.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <QCoreApplication> +#include <QLinkedList> +#include <QLibrary> +#include <QDomDocument> +#include <iostream> +#include <apiextractor.h> +#include "generator.h" +#include "shibokenconfig.h" +#include "cppgenerator.h" +#include "headergenerator.h" +#include "qtdocgenerator.h" + +#ifdef _WINDOWS + #define PATH_SPLITTER ";" +#else + #define PATH_SPLITTER ":" +#endif + +namespace { + +class ArgsHandler +{ +public: + explicit ArgsHandler(const QMap<QString, QString>& other); + virtual ~ArgsHandler(); + + inline QMap<QString, QString>& args() const + { + return *m_args; + } + + inline bool argExists(const QString& s) const + { + return m_args->contains(s); + } + + QString removeArg(const QString& s); + bool argExistsRemove(const QString& s); + + inline QString argValue(const QString& s) const + { + return m_args->value(s); + } + + inline bool noArgs() const + { + return m_args->isEmpty(); + } + +private: + QMap<QString, QString>* m_args; +}; + +ArgsHandler::ArgsHandler(const QMap<QString, QString>& other) + : m_args(new QMap<QString, QString>(other)) +{ +} + +ArgsHandler::~ArgsHandler() +{ + delete m_args; +} + +QString ArgsHandler::removeArg(const QString& s) +{ + QString retval; + + if (argExists(s)) { + retval = argValue(s); + m_args->remove(s); + } + + return retval; +} + +bool ArgsHandler::argExistsRemove(const QString& s) +{ + bool retval = false; + + if (argExists(s)) { + retval = true; + m_args->remove(s); + } + + return retval; +} + +} + +static void printOptions(QTextStream& s, const QMap<QString, QString>& options) +{ + QMap<QString, QString>::const_iterator it = options.constBegin(); + s.setFieldAlignment(QTextStream::AlignLeft); + for (; it != options.constEnd(); ++it) { + s << " --"; + s.setFieldWidth(38); + s << it.key() << it.value(); + s.setFieldWidth(0); + s << endl; + } +} + +typedef void (*getGeneratorsFunc)(QLinkedList<Generator*>*); + +static bool processProjectFile(QFile& projectFile, QMap<QString, QString>& args) +{ + QByteArray line = projectFile.readLine().trimmed(); + if (line.isEmpty() || line != "[generator-project]") + return false; + + QStringList includePaths; + QStringList typesystemPaths; + QStringList apiVersions; + + while (!projectFile.atEnd()) { + line = projectFile.readLine().trimmed(); + if (line.isEmpty()) + continue; + + int split = line.indexOf("="); + QString key; + QString value; + if (split > 0) { + key = line.left(split - 1).trimmed(); + value = line.mid(split + 1).trimmed(); + } else { + key = line; + } + + if (key == "include-path") + includePaths << QDir::toNativeSeparators(value); + else if (key == "typesystem-path") + typesystemPaths << QDir::toNativeSeparators(value); + else if (key == "api-version") + apiVersions << value; + else if (key == "header-file") + args["arg-1"] = value; + else if (key == "typesystem-file") + args["arg-2"] = value; + else + args[key] = value; + } + + if (!includePaths.isEmpty()) + args["include-paths"] = includePaths.join(PATH_SPLITTER); + + if (!typesystemPaths.isEmpty()) + args["typesystem-paths"] = typesystemPaths.join(PATH_SPLITTER); + if (!apiVersions.isEmpty()) + args["api-version"] = apiVersions.join("|"); + return true; +} + +static QMap<QString, QString> getInitializedArguments() +{ + QMap<QString, QString> args; + QStringList arguments = QCoreApplication::arguments(); + QString appName = arguments.first(); + arguments.removeFirst(); + + QString projectFileName; + foreach (const QString& arg, arguments) { + if (arg.startsWith("--project-file")) { + int split = arg.indexOf("="); + if (split > 0) + projectFileName = arg.mid(split + 1).trimmed(); + break; + } + } + + if (projectFileName.isNull()) + return args; + + if (!QFile::exists(projectFileName)) { + std::cerr << qPrintable(appName) << ": Project file \""; + std::cerr << qPrintable(projectFileName) << "\" not found."; + std::cerr << std::endl; + return args; + } + + QFile projectFile(projectFileName); + if (!projectFile.open(QIODevice::ReadOnly)) + return args; + + if (!processProjectFile(projectFile, args)) { + std::cerr << qPrintable(appName) << ": first line of project file \""; + std::cerr << qPrintable(projectFileName) << "\" must be the string \"[generator-project]\""; + std::cerr << std::endl; + return args; + } + + return args; +} + +static QMap<QString, QString> getCommandLineArgs() +{ + QMap<QString, QString> args = getInitializedArguments(); + QStringList arguments = QCoreApplication::arguments(); + arguments.removeFirst(); + + int argNum = 0; + foreach (QString arg, arguments) { + arg = arg.trimmed(); + if (arg.startsWith("--")) { + int split = arg.indexOf("="); + if (split > 0) + args[arg.mid(2).left(split-2)] = arg.mid(split + 1).trimmed(); + else + args[arg.mid(2)] = QString(); + } else if (arg.startsWith("-")) { + args[arg.mid(1)] = QString(); + } else { + argNum++; + args[QString("arg-%1").arg(argNum)] = arg; + } + } + return args; +} + +void printUsage(const GeneratorList& generators) +{ + QTextStream s(stdout); + s << "Usage:\n " + << "shiboken [options] header-file typesystem-file\n\n" + << "General options:\n"; + QMap<QString, QString> generalOptions; + generalOptions.insert("project-file=<file>", "text file containing a description of the binding project. Replaces and overrides command line arguments"); + generalOptions.insert("debug-level=[sparse|medium|full]", "Set the debug level"); + generalOptions.insert("silent", "Avoid printing any message"); + generalOptions.insert("help", "Display this help and exit"); + generalOptions.insert("no-suppress-warnings", "Show all warnings"); + generalOptions.insert("output-directory=<path>", "The directory where the generated files will be written"); + generalOptions.insert("include-paths=<path>[" PATH_SPLITTER "<path>" PATH_SPLITTER "...]", "Include paths used by the C++ parser"); + generalOptions.insert("typesystem-paths=<path>[" PATH_SPLITTER "<path>" PATH_SPLITTER "...]", "Paths used when searching for typesystems"); + generalOptions.insert("documentation-only", "Do not generates any code, just the documentation"); + generalOptions.insert("license-file=<license-file>", "File used for copyright headers of generated files"); + generalOptions.insert("version", "Output version information and exit"); + generalOptions.insert("generator-set=<\"generator module\">", "generator-set to be used. e.g. qtdoc"); + generalOptions.insert("api-version=<\"package mask\">,<\"version\">", "Specify the supported api version used to generate the bindings"); + generalOptions.insert("drop-type-entries=\"<TypeEntry0>[;TypeEntry1;...]\"", "Semicolon separated list of type system entries (classes, namespaces, global functions and enums) to be dropped from generation."); + printOptions(s, generalOptions); + + foreach (Generator* generator, generators) { + QMap<QString, QString> options = generator->options(); + if (!options.isEmpty()) { + s << endl << generator->name() << " options:\n"; + printOptions(s, generator->options()); + } + } +} + +static inline void printVerAndBanner() +{ + std::cout << "shiboken v" SHIBOKEN_VERSION << std::endl; + std::cout << "Copyright (C) 2009-2012 Nokia Corporation and/or its subsidiary(-ies)" << std::endl; +} + +static inline void errorPrint(const QString& s, + const bool& verAndBanner = false) +{ + if (verAndBanner) + printVerAndBanner(); + + std::cerr << s.toAscii().constData() << std::endl; +} + +int main(int argc, char *argv[]) +{ + // needed by qxmlpatterns + QCoreApplication app(argc, argv); + + // Store command arguments in a map + QMap<QString, QString> args = getCommandLineArgs(); + ArgsHandler argsHandler(args); + GeneratorList generators; + + if (argsHandler.argExistsRemove("version")) { + printVerAndBanner(); + return EXIT_SUCCESS; + } + + QString generatorSet = argsHandler.removeArg("generator-set"); + // Also check "generatorSet" command line argument for backward compatibility. + if (generatorSet.isEmpty()) + generatorSet = argsHandler.removeArg("generatorSet"); + + // Pre-defined generator sets. + if (generatorSet == "qtdoc") { + generators << new QtDocGenerator; + } else if (generatorSet.isEmpty() || generatorSet == "shiboken") { + generators << new CppGenerator << new HeaderGenerator; + } else { + errorPrint("shiboken: Unknown generator set, try \"shiboken\" or \"qtdoc\"."); + return EXIT_FAILURE; + } + + if (argsHandler.argExistsRemove("help")) { + printUsage(generators); + return EXIT_SUCCESS; + } + + QString licenseComment; + QString licenseFileName = argsHandler.removeArg("license-file"); + if (!licenseFileName.isEmpty()) { + if (QFile::exists(licenseFileName)) { + QFile licenseFile(licenseFileName); + if (licenseFile.open(QIODevice::ReadOnly)) + licenseComment = licenseFile.readAll(); + } else { + errorPrint(QString("Couldn't find the file containing the license heading: %1"). + arg(qPrintable(licenseFileName))); + return EXIT_FAILURE; + } + } + + QString outputDirectory = argsHandler.removeArg("output-directory"); + if (outputDirectory.isEmpty()) + outputDirectory = "out"; + + if (!QDir(outputDirectory).exists()) { + if (!QDir().mkpath(outputDirectory)) { + ReportHandler::warning("Can't create output directory: "+outputDirectory); + return EXIT_FAILURE; + } + } + + // Create and set-up API Extractor + ApiExtractor extractor; + extractor.setLogDirectory(outputDirectory); + + if (argsHandler.argExistsRemove("silent")) { + extractor.setSilent(true); + } else { + QString level = argsHandler.removeArg("debug-level"); + if (!level.isEmpty()) { + if (level == "sparse") + extractor.setDebugLevel(ReportHandler::SparseDebug); + else if (level == "medium") + extractor.setDebugLevel(ReportHandler::MediumDebug); + else if (level == "full") + extractor.setDebugLevel(ReportHandler::FullDebug); + } + } + if (argsHandler.argExistsRemove("no-suppress-warnings")) + extractor.setSuppressWarnings(false); + + if (argsHandler.argExists("api-version")) { + QStringList versions = argsHandler.removeArg("api-version").split("|"); + foreach (QString fullVersion, versions) { + QStringList parts = fullVersion.split(","); + QString package; + QString version; + package = parts.count() == 1 ? "*" : parts.first(); + version = parts.last(); + extractor.setApiVersion(package, version.toAscii()); + } + } + + if (argsHandler.argExists("drop-type-entries")) + extractor.setDropTypeEntries(argsHandler.removeArg("drop-type-entries")); + + QString path = argsHandler.removeArg("typesystem-paths"); + if (!path.isEmpty()) + extractor.addTypesystemSearchPath(path.split(PATH_SPLITTER)); + + path = argsHandler.removeArg("include-paths"); + if (!path.isEmpty()) + extractor.addIncludePath(path.split(PATH_SPLITTER)); + + QString cppFileName = argsHandler.removeArg("arg-1"); + QString typeSystemFileName = argsHandler.removeArg("arg-2"); + + /* Make sure to remove the project file's arguments (if any) and + * --project-file, also the arguments of each generator before + * checking if there isn't any existing arguments in argsHandler. + */ + argsHandler.removeArg("project-file"); + QMap<QString, QString> projectFileArgs = getInitializedArguments(); + if (!projectFileArgs.isEmpty()) { + QMap<QString, QString>::const_iterator it = + projectFileArgs.constBegin(); + for ( ; it != projectFileArgs.constEnd(); ++it) + argsHandler.removeArg(it.key()); + } + foreach (Generator* generator, generators) { + QMap<QString, QString> options = generator->options(); + if (!options.isEmpty()) { + QMap<QString, QString>::const_iterator it = options.constBegin(); + for ( ; it != options.constEnd(); ++it) + argsHandler.removeArg(it.key()); + } + } + + if (!argsHandler.noArgs()) { + errorPrint("shiboken: Called with wrong arguments."); + std::cout << "Note: use --help option for more information." << std::endl; + return EXIT_FAILURE; + } + + extractor.setCppFileName(cppFileName); + extractor.setTypeSystem(typeSystemFileName); + if (!extractor.run()) + return EXIT_FAILURE; + + if (!extractor.classCount()) + ReportHandler::warning("No C++ classes found!"); + + foreach (Generator* g, generators) { + g->setOutputDirectory(outputDirectory); + g->setLicenseComment(licenseComment); + if (g->setup(extractor, args)) + g->generate(); + } + qDeleteAll(generators); + + ReportHandler::flush(); + std::cout << "Done, " << ReportHandler::warningCount(); + std::cout << " warnings (" << ReportHandler::suppressedCount() << " known issues)"; + std::cout << std::endl; +} |