aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken2/generator/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/shiboken2/generator/main.cpp')
-rw-r--r--sources/shiboken2/generator/main.cpp529
1 files changed, 529 insertions, 0 deletions
diff --git a/sources/shiboken2/generator/main.cpp b/sources/shiboken2/generator/main.cpp
new file mode 100644
index 000000000..874540e54
--- /dev/null
+++ b/sources/shiboken2/generator/main.cpp
@@ -0,0 +1,529 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of PySide2.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QCoreApplication>
+#include <QElapsedTimer>
+#include <QLinkedList>
+#include <QLibrary>
+#include <QtCore/QFile>
+#include <QtCore/QDir>
+#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();
+ }
+
+ QString errorMessage() const;
+
+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;
+}
+
+QString ArgsHandler::errorMessage() const
+{
+ typedef QMap<QString, QString>::ConstIterator StringMapConstIt;
+
+ QString message;
+ QTextStream str(&message);
+ str << "shiboken: Called with wrong arguments:";
+ for (StringMapConstIt it = m_args->cbegin(), end = m_args->cend(); it != end; ++it) {
+ str << ' ' << it.key();
+ if (!it.value().isEmpty())
+ str << ' ' << it.value();
+ }
+ str << "\nCommand line: " << QCoreApplication::arguments().join(QLatin1Char(' '));
+ return message;
+}
+}
+
+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('=');
+ QByteArray key;
+ QString value;
+ if (split > 0) {
+ key = line.left(split - 1).trimmed();
+ value = QString::fromUtf8(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.insert(QLatin1String("arg-1"), value);
+ else if (key == "typesystem-file")
+ args.insert(QLatin1String("arg-2"), value);
+ else
+ args.insert(QString::fromUtf8(key), value);
+ }
+
+ if (!includePaths.isEmpty())
+ args.insert(QLatin1String("include-paths"), includePaths.join(QLatin1String(PATH_SPLITTER)));
+
+ if (!typesystemPaths.isEmpty())
+ args.insert(QLatin1String("typesystem-paths"), typesystemPaths.join(QLatin1String(PATH_SPLITTER)));
+ if (!apiVersions.isEmpty())
+ args.insert(QLatin1String("api-version"), apiVersions.join(QLatin1Char('|')));
+ 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(QLatin1String("--project-file"))) {
+ int split = arg.indexOf(QLatin1Char('='));
+ 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 (const QString &carg, arguments) {
+ const QString &arg = carg.trimmed();
+ if (arg.startsWith(QLatin1String("--"))) {
+ int split = arg.indexOf(QLatin1Char('='));
+ 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(QLatin1Char('-'))) {
+ args[arg.mid(1)] = QString();
+ } else {
+ argNum++;
+ args[QString::fromLatin1("arg-%1").arg(argNum)] = arg;
+ }
+ }
+ return args;
+}
+
+static inline Generators docGenerators()
+{
+ Generators result;
+#ifdef DOCSTRINGS_ENABLED
+ result.append(GeneratorPtr(new QtDocGenerator));
+#endif
+ return result;
+}
+
+static inline Generators shibokenGenerators()
+{
+ Generators result;
+ result << GeneratorPtr(new CppGenerator) << GeneratorPtr(new HeaderGenerator);
+ return result;
+}
+
+void printUsage()
+{
+ QTextStream s(stdout);
+ s << "Usage:\n "
+ << "shiboken [options] header-file typesystem-file\n\n"
+ << "General options:\n";
+ QMap<QString, QString> generalOptions;
+ generalOptions.insert(QLatin1String("project-file=<file>"),
+ QLatin1String("text file containing a description of the binding project. Replaces and overrides command line arguments"));
+ generalOptions.insert(QLatin1String("debug-level=[sparse|medium|full]"),
+ QLatin1String("Set the debug level"));
+ generalOptions.insert(QLatin1String("silent"),
+ QLatin1String("Avoid printing any message"));
+ generalOptions.insert(QLatin1String("help"),
+ QLatin1String("Display this help and exit"));
+ generalOptions.insert(QLatin1String("no-suppress-warnings"),
+ QLatin1String("Show all warnings"));
+ generalOptions.insert(QLatin1String("output-directory=<path>"),
+ QLatin1String("The directory where the generated files will be written"));
+ generalOptions.insert(QLatin1String("include-paths=<path>[" PATH_SPLITTER "<path>" PATH_SPLITTER "...]"),
+ QLatin1String("Include paths used by the C++ parser"));
+ generalOptions.insert(QLatin1String("typesystem-paths=<path>[" PATH_SPLITTER "<path>" PATH_SPLITTER "...]"),
+ QLatin1String("Paths used when searching for typesystems"));
+ generalOptions.insert(QLatin1String("documentation-only"),
+ QLatin1String("Do not generates any code, just the documentation"));
+ generalOptions.insert(QLatin1String("license-file=<license-file>"),
+ QLatin1String("File used for copyright headers of generated files"));
+ generalOptions.insert(QLatin1String("version"),
+ QLatin1String("Output version information and exit"));
+ generalOptions.insert(QLatin1String("generator-set=<\"generator module\">"),
+ QLatin1String("generator-set to be used. e.g. qtdoc"));
+ generalOptions.insert(QLatin1String("api-version=<\"package mask\">,<\"version\">"),
+ QLatin1String("Specify the supported api version used to generate the bindings"));
+ generalOptions.insert(QLatin1String("drop-type-entries=\"<TypeEntry0>[;TypeEntry1;...]\""),
+ QLatin1String("Semicolon separated list of type system entries (classes, namespaces, global functions and enums) to be dropped from generation."));
+ printOptions(s, generalOptions);
+
+ const Generators generators = shibokenGenerators() + docGenerators();
+ foreach (const GeneratorPtr &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) 2016 The Qt Company Ltd." << std::endl;
+}
+
+static inline void errorPrint(const QString& s)
+{
+ QStringList arguments = QCoreApplication::arguments();
+ arguments.pop_front();
+ std::cerr << "shiboken: " << qPrintable(s)
+ << "\nCommand line: " << qPrintable(arguments.join(QLatin1Char(' '))) << '\n';
+}
+
+static QString msgInvalidVersion(const QString &package, const QString &version)
+{
+ return QLatin1String("Invalid version \"") + version
+ + QLatin1String("\" specified for package ") + package + QLatin1Char('.');
+}
+
+int main(int argc, char *argv[])
+{
+ QElapsedTimer timer;
+ timer.start();
+ // needed by qxmlpatterns
+ QCoreApplication app(argc, argv);
+ ReportHandler::install();
+ qCDebug(lcShiboken()).noquote().nospace() << QCoreApplication::arguments().join(QLatin1Char(' '));
+
+ // Store command arguments in a map
+ QMap<QString, QString> args = getCommandLineArgs();
+ ArgsHandler argsHandler(args);
+ Generators generators;
+
+ if (argsHandler.argExistsRemove(QLatin1String("version"))) {
+ printVerAndBanner();
+ return EXIT_SUCCESS;
+ }
+
+ QString generatorSet = argsHandler.removeArg(QLatin1String("generator-set"));
+ // Also check QLatin1String("generatorSet") command line argument for backward compatibility.
+ if (generatorSet.isEmpty())
+ generatorSet = argsHandler.removeArg(QLatin1String("generatorSet"));
+
+ // Pre-defined generator sets.
+ if (generatorSet == QLatin1String("qtdoc")) {
+ generators = docGenerators();
+ if (generators.isEmpty()) {
+ errorPrint(QLatin1String("Doc strings extractions was not enabled in this shiboken build."));
+ return EXIT_FAILURE;
+ }
+ } else if (generatorSet.isEmpty() || generatorSet == QLatin1String("shiboken")) {
+ generators = shibokenGenerators();
+ } else {
+ errorPrint(QLatin1String("Unknown generator set, try \"shiboken\" or \"qtdoc\"."));
+ return EXIT_FAILURE;
+ }
+
+ if (argsHandler.argExistsRemove(QLatin1String("help"))) {
+ printUsage();
+ return EXIT_SUCCESS;
+ }
+
+ QString licenseComment;
+ QString licenseFileName = argsHandler.removeArg(QLatin1String("license-file"));
+ if (!licenseFileName.isEmpty()) {
+ if (QFile::exists(licenseFileName)) {
+ QFile licenseFile(licenseFileName);
+ if (licenseFile.open(QIODevice::ReadOnly))
+ licenseComment = QString::fromUtf8(licenseFile.readAll());
+ } else {
+ errorPrint(QStringLiteral("Couldn't find the file containing the license heading: %1").
+ arg(licenseFileName));
+ return EXIT_FAILURE;
+ }
+ }
+
+ QString outputDirectory = argsHandler.removeArg(QLatin1String("output-directory"));
+ if (outputDirectory.isEmpty())
+ outputDirectory = QLatin1String("out");
+
+ if (!QDir(outputDirectory).exists()) {
+ if (!QDir().mkpath(outputDirectory)) {
+ qCWarning(lcShiboken).noquote().nospace()
+ << "Can't create output directory: " << QDir::toNativeSeparators(outputDirectory);
+ return EXIT_FAILURE;
+ }
+ }
+
+ // Create and set-up API Extractor
+ ApiExtractor extractor;
+ extractor.setLogDirectory(outputDirectory);
+
+ if (argsHandler.argExistsRemove(QLatin1String("silent"))) {
+ extractor.setSilent(true);
+ } else {
+ QString level = argsHandler.removeArg(QLatin1String("debug-level"));
+ if (!level.isEmpty()) {
+ if (level == QLatin1String("sparse"))
+ extractor.setDebugLevel(ReportHandler::SparseDebug);
+ else if (level == QLatin1String("medium"))
+ extractor.setDebugLevel(ReportHandler::MediumDebug);
+ else if (level == QLatin1String("full"))
+ extractor.setDebugLevel(ReportHandler::FullDebug);
+ }
+ }
+ if (argsHandler.argExistsRemove(QLatin1String("no-suppress-warnings")))
+ extractor.setSuppressWarnings(false);
+
+ if (argsHandler.argExists(QLatin1String("api-version"))) {
+ QStringList versions = argsHandler.removeArg(QLatin1String("api-version")).split(QLatin1Char('|'));
+ foreach (const QString &fullVersion, versions) {
+ QStringList parts = fullVersion.split(QLatin1Char(','));
+ QString package;
+ QString version;
+ // avoid constFirst to stay Qt 5.5 compatible
+ package = parts.count() == 1 ? QLatin1String("*") : parts.first();
+ version = parts.last();
+ if (!extractor.setApiVersion(package, version)) {
+ errorPrint(msgInvalidVersion(package, version));
+ return EXIT_FAILURE;
+ }
+ }
+ }
+
+ if (argsHandler.argExists(QLatin1String("drop-type-entries")))
+ extractor.setDropTypeEntries(argsHandler.removeArg(QLatin1String("drop-type-entries")));
+
+ QString path = argsHandler.removeArg(QLatin1String("typesystem-paths"));
+ if (!path.isEmpty())
+ extractor.addTypesystemSearchPath(path.split(QLatin1String(PATH_SPLITTER)));
+
+ path = argsHandler.removeArg(QLatin1String("include-paths"));
+ if (!path.isEmpty())
+ extractor.addIncludePath(path.split(QLatin1String(PATH_SPLITTER)));
+
+ QString cppFileName = argsHandler.removeArg(QLatin1String("arg-1"));
+ QString typeSystemFileName = argsHandler.removeArg(QLatin1String("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(QLatin1String("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 (const GeneratorPtr &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(argsHandler.errorMessage());
+ std::cout << "Note: use --help option for more information." << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ extractor.setCppFileName(cppFileName);
+ extractor.setTypeSystem(typeSystemFileName);
+ if (!extractor.run()) {
+ errorPrint(QLatin1String("Error running ApiExtractor."));
+ return EXIT_FAILURE;
+ }
+
+ if (!extractor.classCount())
+ qCWarning(lcShiboken) << "No C++ classes found!";
+
+ qCDebug(lcShiboken) << extractor;
+
+ foreach (const GeneratorPtr &g, generators) {
+ g->setOutputDirectory(outputDirectory);
+ g->setLicenseComment(licenseComment);
+ if (g->setup(extractor, args)) {
+ if (!g->generate()) {
+ errorPrint(QLatin1String("Error running generator: ")
+ + QLatin1String(g->name()) + QLatin1Char('.'));
+ return EXIT_FAILURE;
+ }
+ }
+ }
+
+ QByteArray doneMessage = "Done, " + QByteArray::number(timer.elapsed()) + "ms";
+ if (const int w = ReportHandler::warningCount())
+ doneMessage += ", " + QByteArray::number(w) + " warnings";
+ if (const int sw = ReportHandler::suppressedCount())
+ doneMessage += " (" + QByteArray::number(sw) + " known issues)";
+ qCDebug(lcShiboken()).noquote().nospace() << doneMessage;
+ std::cout << doneMessage.constData() << std::endl;
+
+ return EXIT_SUCCESS;
+}