diff options
author | Marcelo Lira <marcelo.lira@openbossa.org> | 2009-08-25 19:43:06 -0300 |
---|---|---|
committer | Marcelo Lira <marcelo.lira@openbossa.org> | 2009-08-25 19:43:06 -0300 |
commit | fd52957d375eb990a0fabbfab35d36480ee057ae (patch) | |
tree | 3ef09ee06c63dc953dfa25829cc8f4a71bb81dde | |
parent | 8f941405798b67204945d7ec94ef8e63e1535877 (diff) | |
parent | f8fba84d7b0230b1f48109c339197c407757bea1 (diff) |
Merge commit 'mainline/master' into metaclassname
Conflicts:
boostpythongenerator.cpp
21 files changed, 1131 insertions, 358 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e385494f..cf93f1078 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,24 +1,25 @@ -project(boostpythongenerator) +project(generatorrunner) cmake_minimum_required(VERSION 2.6) find_package(Qt4 4.5.0 REQUIRED) find_package(ApiExtractor REQUIRED) +set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)" ) +set(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})" FORCE) + + +# lib generator version +set(generator_MAJOR_VERSION "0") +set(generator_MINOR_VERSION "1") +set(generator_VERSION "${generator_MAJOR_VERSION}.${generator_MINOR_VERSION}") + add_definitions(${QT_DEFINITIONS}) set(boostpythongenerator_VERSION 0.2) configure_file(boostpythongeneratorversion.h.in ${CMAKE_CURRENT_BINARY_DIR}/boostpythongeneratorversion.h @ONLY) set(CMAKE_BUILD_TYPE Debug) -set(boostpythongenerator_SRC -boostpythongenerator.cpp -convertergenerator.cpp -docgenerator.cpp -hppgenerator.cpp -cppgenerator.cpp -) - include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${APIEXTRACTOR_INCLUDE_DIR} @@ -26,13 +27,15 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${QT_INCLUDE_DIR} ${QT_QTCORE_INCLUDE_DIR}) -add_library(libboostpythongenerator STATIC ${boostpythongenerator_SRC}) -target_link_libraries(libboostpythongenerator +add_library(genrunner SHARED generator.cpp) +set_target_properties(genrunner PROPERTIES SOVERSION ${generator_VERSION} VERSION ${generator_MAJOR_VERSION}) +target_link_libraries(genrunner ${QT_QTCORE_LIBRARY} ${APIEXTRACTOR_LIBRARY}) + +add_executable(generatorrunner main.cpp) +target_link_libraries(generatorrunner + genrunner ${APIEXTRACTOR_LIBRARY} - ${QT_QTCORE_LIBRARY} - ${QT_QTXML_LIBRARY}) -add_executable(boostpythongenerator main.cpp) -target_link_libraries(boostpythongenerator libboostpythongenerator) + ${QT_QTCORE_LIBRARY}) # uninstall target configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake" @@ -42,11 +45,6 @@ add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") -# "make dist", in fact "make package_source" -#set(CPACK_SOURCE_PACKAGE_FILE_NAME "boostpythongenerator-${boostpythongenerator_VERSION}") -#set(CPACK_SOURCE_GENERATOR TGZ) -#set(CPACK_SOURCE_IGNORE_FILES "~$" ".svn" "debian/" "build/" ".swp$" "*.kdev4") -#include(CPack) set(ARCHIVE_NAME ${CMAKE_PROJECT_NAME}-${boostpythongenerator_VERSION}) add_custom_target(dist @@ -54,9 +52,11 @@ add_custom_target(dist | bzip2 > ${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar.bz2 WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) -install(TARGETS boostpythongenerator DESTINATION bin) +install(TARGETS genrunner DESTINATION ${LIB_INSTALL_DIR}) +install(TARGETS generatorrunner DESTINATION bin) +install(FILES generator.h DESTINATION include) enable_testing() -#add_subdirectory(libbindgen) -add_subdirectory(tests) +add_subdirectory(generators) +add_subdirectory(tests) diff --git a/generator.cpp b/generator.cpp new file mode 100644 index 000000000..80610c401 --- /dev/null +++ b/generator.cpp @@ -0,0 +1,439 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 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 "generator.h" +#include "reporthandler.h" +#include "fileout.h" +#include "apiextractor.h" + +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QDebug> + +Generator::Generator() : m_numGenerated(0), m_numGeneratedWritten(0) +{} + +Generator::~Generator() +{ +} + +bool Generator::setup(const ApiExtractor& extractor, const QMap< QString, QString > args) +{ + m_globalEnums = extractor.globalEnums(); + m_globalFunctions = extractor.globalFunctions(); + m_classes = extractor.classes(); + m_primitiveTypes = extractor.primitiveTypes(); + m_containerTypes = extractor.containerTypes(); + + // FIXME: Avoid this ugly hack to get the package name.. and... why the name "package"!? + foreach (const AbstractMetaClass* cppClass, m_classes) { + if (m_packageName.isEmpty() + && cppClass->typeEntry()->generateCode() + && !cppClass->package().isEmpty()) { + m_packageName = cppClass->package(); + break; + } + } + // does anyone use this? + m_qmetatypeDeclaredTypenames = extractor.qtMetaTypeDeclaredTypeNames(); + return doSetup(args); +} + +QMap< QString, QString > Generator::options() const +{ + return QMap<QString, QString>(); +} + +AbstractMetaClassList Generator::classes() const +{ + return m_classes; +} + +AbstractMetaFunctionList Generator::globalFunctions() const +{ + return m_globalFunctions; +} + +AbstractMetaEnumList Generator::globalEnums() const +{ + return m_globalEnums; +} + +QList<const PrimitiveTypeEntry*> Generator::primitiveTypes() const +{ + return m_primitiveTypes; +} + +QList<const ContainerTypeEntry*> Generator::containerTypes() const +{ + return m_containerTypes; +} + +/// Returns the output directory +QString Generator::outputDirectory() const +{ + return m_outDir; +} + +/// Set the output directory +void Generator::setOutputDirectory(const QString &outDir) +{ + m_outDir = outDir; +} + +void Generator::generate() +{ + foreach (AbstractMetaClass *cls, m_classes) { + if (!shouldGenerate(cls)) + continue; + + QString fileName = fileNameForClass(cls); + if (fileName.isNull()) + continue; + ReportHandler::debugSparse(QString("generating: %1").arg(fileName)); + + FileOut fileOut(outputDirectory() + '/' + subDirectoryForClass(cls) + '/' + fileName); + generateClass(fileOut.stream, cls); + + if (fileOut.done()) + ++m_numGeneratedWritten; + ++m_numGenerated; + } + finishGeneration(); +} + +bool Generator::shouldGenerate(const AbstractMetaClass* metaClass) const +{ + return metaClass->typeEntry()->codeGeneration() & TypeEntry::GenerateTargetLang; +} + +void Generator::verifyDirectoryFor(const QFile &file) +{ + QDir dir = QFileInfo(file).dir(); + if (!dir.exists()) { + if (!dir.mkpath(dir.absolutePath())) + ReportHandler::warning(QString("unable to create directory '%1'") + .arg(dir.absolutePath())); + } +} + +bool Generator::hasDefaultConstructor(const AbstractMetaType *type) +{ + QString full_name = type->typeEntry()->qualifiedTargetLangName(); + QString class_name = type->typeEntry()->targetLangName(); + + foreach (const AbstractMetaClass *cls, m_classes) { + if (cls->typeEntry()->qualifiedTargetLangName() == full_name) { + AbstractMetaFunctionList functions = cls->functions(); + foreach (const AbstractMetaFunction *function, functions) { + if (function->arguments().isEmpty() && function->name() == class_name) + return true; + } + return false; + } + } + return false; +} + +void Generator::replaceTemplateVariables(QString &code, const AbstractMetaFunction *func) +{ + const AbstractMetaClass *cpp_class = func->ownerClass(); + code.replace("%TYPE", cpp_class->name()); + + foreach (AbstractMetaArgument *arg, func->arguments()) + code.replace("%" + QString::number(arg->argumentIndex() + 1), arg->argumentName()); + + //template values + code.replace("%RETURN_TYPE", translateType(func->type(), cpp_class)); + code.replace("%FUNCTION_NAME", func->originalName()); + + if (code.contains("%ARGUMENT_NAMES")) { + QString str; + QTextStream aux_stream(&str); + writeArgumentNames(aux_stream, func, Generator::SkipRemovedArguments); + code.replace("%ARGUMENT_NAMES", str); + } + + if (code.contains("%ARGUMENTS")) { + QString str; + QTextStream aux_stream(&str); + writeFunctionArguments(aux_stream, func, Generator::SkipDefaultValues | Generator::SkipRemovedArguments); + code.replace("%ARGUMENTS", str); + } +} + +AbstractMetaFunctionList Generator::queryFunctions(const AbstractMetaClass *cppClass, bool allFunctions) +{ + AbstractMetaFunctionList result; + + if (allFunctions) { + int default_flags = AbstractMetaClass::NormalFunctions | AbstractMetaClass::Visible; + default_flags |= cppClass->isInterface() ? 0 : AbstractMetaClass::ClassImplements; + + // Constructors + result = cppClass->queryFunctions(AbstractMetaClass::Constructors | + default_flags); + + // put enum constructor first to avoid conflict with int contructor + result = sortContructor(result); + + // Final functions + result += cppClass->queryFunctions(AbstractMetaClass::FinalInTargetLangFunctions | + AbstractMetaClass::NonStaticFunctions | + default_flags); + + //virtual + result += cppClass->queryFunctions(AbstractMetaClass::VirtualInTargetLangFunctions | + AbstractMetaClass::NonStaticFunctions | + default_flags); + + // Static functions + result += cppClass->queryFunctions(AbstractMetaClass::StaticFunctions | default_flags); + + // Empty, private functions, since they aren't caught by the other ones + result += cppClass->queryFunctions(AbstractMetaClass::Empty | + AbstractMetaClass::Invisible | default_flags); + // Signals + result += cppClass->queryFunctions(AbstractMetaClass::Signals | default_flags); + } else { + result = cppClass->functionsInTargetLang(); + } + + return result; +} + +AbstractMetaFunctionList Generator::filterFunctions(const AbstractMetaClass *cppClass) +{ + AbstractMetaFunctionList lst = queryFunctions(cppClass, true); + foreach (AbstractMetaFunction *func, lst) { + //skip signals + if (func->isSignal() || + func->isDestructor() || + (func->isModifiedRemoved() && !func->isAbstract())) { + lst.removeOne(func); + } + } + + //virtual not implemented in current class + AbstractMetaFunctionList virtual_lst = cppClass->queryFunctions(AbstractMetaClass::VirtualFunctions); + foreach (AbstractMetaFunction *func, virtual_lst) { + if ((func->implementingClass() != cppClass) && + !lst.contains(func)) { + lst.append(func); + } + } + + //append global operators + foreach (AbstractMetaFunction *func , queryGlobalOperators(cppClass)) { + if (!lst.contains(func)) + lst.append(func); + } + + return lst; + //return cpp_class->functions(); +} + +AbstractMetaFunctionList Generator::queryGlobalOperators(const AbstractMetaClass *cppClass) +{ + AbstractMetaFunctionList result; + + foreach (AbstractMetaFunction *func, cppClass->functions()) { + if (func->isInGlobalScope() && func->isOperatorOverload()) + result.append(func); + } + return result; +} + +AbstractMetaFunctionList Generator::sortContructor(AbstractMetaFunctionList list) +{ + AbstractMetaFunctionList result; + + foreach (AbstractMetaFunction *func, list) { + bool inserted = false; + foreach (AbstractMetaArgument *arg, func->arguments()) { + if (arg->type()->isFlags() || arg->type()->isEnum()) { + result.push_back(func); + inserted = true; + break; + } + } + if (!inserted) + result.push_front(func); + } + + return result; +} + +FunctionModificationList Generator::functionModifications(const AbstractMetaFunction *metaFunction) +{ + FunctionModificationList mods; + const AbstractMetaClass *cls = metaFunction->implementingClass(); + while (cls) { + mods += metaFunction->modifications(cls); + + if (cls == cls->baseClass()) + break; + cls = cls->baseClass(); + } + return mods; +} + +static QString formattedCodeHelper(QTextStream &s, Indentor &indentor, QStringList &lines) +{ + bool multilineComment = false; + bool lastEmpty = true; + QString lastLine; + while (!lines.isEmpty()) { + const QString line = lines.takeFirst().trimmed(); + if (line.isEmpty()) { + if (!lastEmpty) + s << endl; + lastEmpty = true; + continue; + } else + lastEmpty = false; + + if (line.startsWith("/*")) + multilineComment = true; + + if (multilineComment) { + s << indentor; + if (line.startsWith("*")) + s << " "; + s << line << endl; + if (line.endsWith("*/")) + multilineComment = false; + } else if (line.startsWith("}")) + return line; + else if (line.endsWith("")) { + s << indentor << line << endl; + return 0; + } else if (line.endsWith("{")) { + s << indentor << line << endl; + QString tmp; + { + Indentation indent(indentor); + tmp = formattedCodeHelper(s, indentor, lines); + } + if (!tmp.isNull()) + s << indentor << tmp << endl; + + lastLine = tmp; + continue; + } else { + s << indentor; + if (!lastLine.isEmpty() && + !lastLine.endsWith(";") && + !line.startsWith("@") && + !line.startsWith("//") && + !lastLine.startsWith("//") && + !lastLine.endsWith("}") && + !line.startsWith("{")) + s << " "; + s << line << endl; + } + lastLine = line; + } + return 0; +} + +QTextStream& formatCode(QTextStream &s, const QString& code, Indentor &indentor) +{ + QStringList lst(code.split("\n")); + while (!lst.isEmpty()) { + QString tmp = formattedCodeHelper(s, indentor, lst); + if (!tmp.isNull()) + s << indentor << tmp << endl; + + } + s.flush(); + return s; +} + +CodeSnipList Generator::getCodeSnips(const AbstractMetaFunction *func) +{ + CodeSnipList result; + const AbstractMetaClass *cppClass = func->implementingClass(); + while (cppClass) { + foreach (FunctionModification mod, func->modifications(cppClass)) { + if (mod.isCodeInjection()) + result << mod.snips; + } + + if (cppClass == cppClass->baseClass()) + break; + cppClass = cppClass->baseClass(); + } + + return result; +} + +QString Generator::translateType(const AbstractMetaType *cType, + const AbstractMetaClass *context, + int option) const +{ + QString s; + + if (context && cType && + context->typeEntry()->isGenericClass() && + cType->originalTemplateType()) { + qDebug() << "set original templateType" << cType->name(); + cType = cType->originalTemplateType(); + } + + if (!cType) { + s = "void"; + } else if (cType->isArray()) { + s = translateType(cType->arrayElementType(), context) + "[]"; + } else if (cType->isEnum() || cType->isFlags()) { + if (option & Generator::EnumAsInts) + s = "int"; + else + s = cType->cppSignature(); +#if 0 + } else if (c_type->isContainer()) { + qDebug() << "is container" << c_type->cppSignature(); + s = c_type->name(); + if (!(option & SkipTemplateParameters)) { + s += " < "; + QList<AbstractMetaType *> args = c_type->instantiations(); + for (int i = 0; i < args.size(); ++i) { + if (i) + s += ", "; + qDebug() << "container type: " << args.at(i)->cppSignature() << " / " << args.at(i)->instantiations().count(); + s += translateType(args.at(i), context, option); + } + s += " > "; + } +#endif + } else { + s = cType->cppSignature(); + if (cType->isConstant() && (option & Generator::ExcludeConst)) + s.replace("const", ""); + if (cType->isReference() && (option & Generator::ExcludeReference)) + s.replace("&", ""); + } + + return s; +} diff --git a/generator.h b/generator.h new file mode 100644 index 000000000..8040072aa --- /dev/null +++ b/generator.h @@ -0,0 +1,330 @@ +/* + * This file is part of the API Extractor project. + * + * Copyright (C) 2009 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 + * + */ + +#ifndef GENERATOR_H +#define GENERATOR_H + +#include <QtCore/QObject> +#include <QtCore/QDir> +#include <QtCore/QLinkedList> +#include <apiextractor/abstractmetalang.h> + +class ApiExtractor; +class AbstractMetaBuilder; +class QFile; + +#define EXPORT_GENERATOR_PLUGIN(X)\ +extern "C" Q_DECL_EXPORT GeneratorList getGenerators()\ +{\ + return GeneratorList() << X;\ +}\ + +QTextStream& formatCode(QTextStream &s, const QString& code, Indentor &indentor); + +/** + * Base class for all generators. The default implementations does nothing, + * you must subclass this to create your own generators. + */ +class Generator +{ +public: + /// Optiosn used around the generator code + enum Option { + NoOption = 0x00000000, + BoxedPrimitive = 0x00000001, + ExcludeConst = 0x00000002, + ExcludeReference = 0x00000004, + UseNativeIds = 0x00000008, + + EnumAsInts = 0x00000010, + SkipName = 0x00000020, + NoCasts = 0x00000040, + SkipReturnType = 0x00000080, + OriginalName = 0x00000100, + ShowStatic = 0x00000200, + UnderscoreSpaces = 0x00000400, + ForceEnumCast = 0x00000800, + ArrayAsPointer = 0x00001000, + VirtualCall = 0x00002000, + SkipTemplateParameters = 0x00004000, + SkipAttributes = 0x00008000, + OriginalTypeDescription = 0x00010000, + SkipRemovedArguments = 0x00020000, + IncludeDefaultExpression = 0x00040000, + NoReturnStatement = 0x00080000, + NoBlockedSlot = 0x00100000, + + SuperCall = 0x00200000, + + GlobalRefJObject = 0x00100000, + + SkipDefaultValues = 0x00400000, + + WriteSelf = 0x00800000, + ExcludeMethodConst = 0x01000000, + + ForceValueType = ExcludeReference | ExcludeConst + }; + + Generator(); + virtual ~Generator(); + + bool setup(const ApiExtractor& extractor, const QMap<QString, QString> args); + + virtual QMap<QString, QString> options() const; + + /// Returns the classes used to generate the binding code. + AbstractMetaClassList classes() const; + + AbstractMetaFunctionList globalFunctions() const; + + AbstractMetaEnumList globalEnums() const; + + QList<const PrimitiveTypeEntry*> primitiveTypes() const; + + QList<const ContainerTypeEntry*> containerTypes() const; + + /// Returns the output directory + QString outputDirectory() const; + + /// Set the output directory + void setOutputDirectory(const QString &outDir); + + /** + * Start the code generation, be sure to call setClasses before callign this method. + * For each class it creates a QTextStream, call the write method with the current + * class and the associated text stream, then write the text stream contents if needed. + * \see #write + */ + void generate(); + + /// Returns the number of generated items + int numGenerated() + { + return m_numGenerated; + } + + /// Returns the number of generated items written + int numGeneratedAndWritten() + { + return m_numGeneratedWritten; + } + + virtual const char* name() const = 0; + + /// Returns true if the generator should generate any code for the AbstractMetaClass + virtual bool shouldGenerate(const AbstractMetaClass *) const; + + /// Returns the subdirectory used to write the binding code of an AbstractMetaClass. + virtual QString subDirectoryForClass(const AbstractMetaClass* clazz) const = 0; + + /** + * Translate metatypes to binding source format. + * \param metatype a pointer to metatype + * \param context the current meta class + * \param option some extra options + * \return the metatype translated to binding source format + */ + QString translateType(const AbstractMetaType *metatype, + const AbstractMetaClass *context, + int option = NoOption) const; + + /** + * Function used to write the fucntion arguments on the class buffer. + * \param s the class output buffer + * \param metafunction the pointer to metafunction information + * \param count the number of function arguments + * \param options some extra options used during the parser + */ + virtual void writeFunctionArguments(QTextStream &s, + const AbstractMetaFunction *metafunction, + uint options = 0) const = 0; + + virtual void writeArgumentNames(QTextStream &s, + const AbstractMetaFunction *metafunction, + uint options = 0) const = 0; + + void replaceTemplateVariables(QString &code, const AbstractMetaFunction *func); + + bool hasDefaultConstructor(const AbstractMetaType *type); + + // QtScript + QSet<QString> qtMetaTypeDeclaredTypeNames() const + { + return m_qmetatypeDeclaredTypenames; + } + + /** + * Returns the license comment to be prepended to each source file generated. + */ + QString licenseComment() + { + return m_licenseComment; + } + + /** + * Sets the license comment to be prepended to each source file generated. + */ + void setLicenseComment(const QString &licenseComment) + { + m_licenseComment = licenseComment; + } + + /** + * Returns the package name. + */ + QString packageName() + { + return m_packageName; + } + + /** + * Sets the package name. + */ + void setPackageName(const QString &packageName) + { + m_packageName = packageName; + } + + /** + * Retrieves the name of the currently processed module. While package name + * is a complete package idetification, e.g. 'PySide.QtCore', a module name + * represents the last part of the package, e.g. 'QtCore'. + * If the target language separates the modules with characters other than + * dots ('.') the generator subclass must overload this method. + * /return a string representing the last part of a package name + */ + virtual QString moduleName() + { + return QString(m_packageName).remove(0, m_packageName.lastIndexOf('.') + 1); + } + + /// returns the code snips of a function + CodeSnipList getCodeSnips(const AbstractMetaFunction *func); + +protected: + QString m_packageName; + + /** + * Returns the file name used to write the binding code of an AbstractMetaClass. + * /param metaClass the AbstractMetaClass for which the file name must be + * returned + * /return the file name used to write the binding code for the class + */ + virtual QString fileNameForClass(const AbstractMetaClass* metaClass) const = 0; + + static FunctionModificationList functionModifications(const AbstractMetaFunction *meta_function); + AbstractMetaFunctionList filterFunctions(const AbstractMetaClass *cppClass); + AbstractMetaFunctionList queryFunctions(const AbstractMetaClass *cpp_class, bool all_function = false); + AbstractMetaFunctionList queryGlobalOperators(const AbstractMetaClass *cpp_class); + AbstractMetaFunctionList sortContructor(AbstractMetaFunctionList list); + + virtual bool doSetup(const QMap<QString, QString>& args) = 0; + + /** + * Returns the subdirectory path for a given package + * (aka module, aka library) name. + * If the target language separates the package modules with characters other + * than dots ('.') the generator subclass must overload this method. + * /param packageName complete package name for which to return the subdirectory path + * or nothing the use the name of the currently processed package + * /return a string representing the subdirectory path for the given package + */ + virtual QString subDirectoryForPackage(QString packageName = QString()) const + { + if (packageName.isEmpty()) + packageName = m_packageName; + return QString(packageName).replace(".", QDir::separator()); + } + + /** + * Write the bindding code for an AbstractMetaClass. + * This is called by the default implementation of generate method. + * \param s text stream to write the generated output + * \param metaClass the class that should be generated + */ + virtual void generateClass(QTextStream& s, const AbstractMetaClass* metaClass) = 0; + virtual void finishGeneration() = 0; + + void verifyDirectoryFor(const QFile &file); + + int m_numGenerated; + int m_numGeneratedWritten; + +private: + AbstractMetaClassList m_classes; + AbstractMetaFunctionList m_globalFunctions; + AbstractMetaEnumList m_globalEnums; + QString m_outDir; + + QList<const PrimitiveTypeEntry*> m_primitiveTypes; + QList<const ContainerTypeEntry*> m_containerTypes; + + // QtScript + QSet<QString> m_qmetatypeDeclaredTypenames; + + // License comment + QString m_licenseComment; +}; + +typedef QLinkedList<Generator*> GeneratorList; + +/** +* Utility class to store the identation level, use it in a QTextStream. +*/ +class Indentor +{ +public: + Indentor(): + indent(0) {} + int indent; +}; + +/** +* Class that use the RAII idiom to set and unset the identation level. +*/ +class Indentation +{ +public: + Indentation(Indentor &indentor) : indentor(indentor) + { + indentor.indent++; + } + ~Indentation() + { + indentor.indent--; + } + +private: + Indentor &indentor; +}; + +inline QTextStream &operator <<(QTextStream &s, const Indentor &indentor) +{ + for (int i = 0; i < indentor.indent; ++i) + s << " "; + return s; +} + +#endif // GENERATOR_H + diff --git a/generators/CMakeLists.txt b/generators/CMakeLists.txt new file mode 100644 index 000000000..73b5eea22 --- /dev/null +++ b/generators/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(boostpython) +add_subdirectory(qtdoc) diff --git a/generators/boostpython/CMakeLists.txt b/generators/boostpython/CMakeLists.txt new file mode 100644 index 000000000..b29b8cf89 --- /dev/null +++ b/generators/boostpython/CMakeLists.txt @@ -0,0 +1,14 @@ +project(boostpython) + +set(boostpython_generator_SRC +boostpythongenerator.cpp +convertergenerator.cpp +cppgenerator.cpp +hppgenerator.cpp +boostpython.cpp +) + +add_library(boostpython_generator SHARED ${boostpython_generator_SRC}) +target_link_libraries(boostpython_generator ${APIEXTRACTOR_LIBRARY} ${QT_QTCORE_LIBRARY} genrunner) + +install(TARGETS boostpython_generator DESTINATION ${LIB_INSTALL_DIR}) diff --git a/generators/boostpython/boostpython.cpp b/generators/boostpython/boostpython.cpp new file mode 100644 index 000000000..e165f93b9 --- /dev/null +++ b/generators/boostpython/boostpython.cpp @@ -0,0 +1,29 @@ +/* +* This file is part of the API Extractor project. +* +* Copyright (C) 2009 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 "generator.h" +#include "hppgenerator.h" +#include "cppgenerator.h" +#include "convertergenerator.h" + +EXPORT_GENERATOR_PLUGIN(new HppGenerator << new CppGenerator << new ConverterGenerator) diff --git a/boostpythongenerator.cpp b/generators/boostpython/boostpythongenerator.cpp index 732ef3b65..8930c40cf 100644 --- a/boostpythongenerator.cpp +++ b/generators/boostpython/boostpythongenerator.cpp @@ -36,69 +36,6 @@ static Indentor INDENT; static void dump_function(AbstractMetaFunctionList lst); -FunctionModificationList BoostPythonGenerator::functionModifications(const AbstractMetaFunction *metaFunction) -{ - FunctionModificationList mods; - const AbstractMetaClass *cls = metaFunction->implementingClass(); - while (cls) { - mods += metaFunction->modifications(cls); - - if (cls == cls->baseClass()) - break; - cls = cls->baseClass(); - } - return mods; -} - -QString BoostPythonGenerator::translateType(const AbstractMetaType *cType, - const AbstractMetaClass *context, - int option) const -{ - QString s; - - if (context && cType && - context->typeEntry()->isGenericClass() && - cType->originalTemplateType()) { - qDebug() << "set original templateType" << cType->name(); - cType = cType->originalTemplateType(); - } - - if (!cType) { - s = "void"; - } else if (cType->isArray()) { - s = translateType(cType->arrayElementType(), context) + "[]"; - } else if (cType->isEnum() || cType->isFlags()) { - if (option & Generator::EnumAsInts) - s = "int"; - else - s = cType->cppSignature(); -#if 0 - } else if (c_type->isContainer()) { - qDebug() << "is container" << c_type->cppSignature(); - s = c_type->name(); - if (!(option & SkipTemplateParameters)) { - s += " < "; - QList<AbstractMetaType *> args = c_type->instantiations(); - for (int i = 0; i < args.size(); ++i) { - if (i) - s += ", "; - qDebug() << "container type: " << args.at(i)->cppSignature() << " / " << args.at(i)->instantiations().count(); - s += translateType(args.at(i), context, option); - } - s += " > "; - } -#endif - } else { - s = cType->cppSignature(); - if (cType->isConstant() && (option & Generator::ExcludeConst)) - s.replace("const", ""); - if (cType->isReference() && (option & Generator::ExcludeReference)) - s.replace("&", ""); - } - - return s; -} - QString BoostPythonGenerator::getWrapperName(const AbstractMetaClass* metaClass) { QString result = metaClass->typeEntry()->qualifiedCppName().toLower(); @@ -271,77 +208,6 @@ void BoostPythonGenerator::writeArgumentNames(QTextStream &s, } } -AbstractMetaFunctionList BoostPythonGenerator::queryGlobalOperators(const AbstractMetaClass *cppClass) -{ - AbstractMetaFunctionList result; - - foreach (AbstractMetaFunction *func, cppClass->functions()) { - if (func->isInGlobalScope() && func->isOperatorOverload()) - result.append(func); - } - return result; -} - -AbstractMetaFunctionList BoostPythonGenerator::sortContructor(AbstractMetaFunctionList list) -{ - AbstractMetaFunctionList result; - - foreach (AbstractMetaFunction *func, list) { - bool inserted = false; - foreach (AbstractMetaArgument *arg, func->arguments()) { - if (arg->type()->isFlags() || arg->type()->isEnum()) { - result.push_back(func); - inserted = true; - break; - } - } - if (!inserted) - result.push_front(func); - } - - return result; -} - -AbstractMetaFunctionList BoostPythonGenerator::queryFunctions(const AbstractMetaClass *cppClass, bool allFunctions) -{ - AbstractMetaFunctionList result; - - if (allFunctions) { - int default_flags = AbstractMetaClass::NormalFunctions | AbstractMetaClass::Visible; - default_flags |= cppClass->isInterface() ? 0 : AbstractMetaClass::ClassImplements; - - // Constructors - result = cppClass->queryFunctions(AbstractMetaClass::Constructors | - default_flags); - - // put enum constructor first to avoid conflict with int contructor - result = sortContructor(result); - - // Final functions - result += cppClass->queryFunctions(AbstractMetaClass::FinalInTargetLangFunctions | - AbstractMetaClass::NonStaticFunctions | - default_flags); - - //virtual - result += cppClass->queryFunctions(AbstractMetaClass::VirtualInTargetLangFunctions | - AbstractMetaClass::NonStaticFunctions | - default_flags); - - // Static functions - result += cppClass->queryFunctions(AbstractMetaClass::StaticFunctions | default_flags); - - // Empty, private functions, since they aren't caught by the other ones - result += cppClass->queryFunctions(AbstractMetaClass::Empty | - AbstractMetaClass::Invisible | default_flags); - // Signals - result += cppClass->queryFunctions(AbstractMetaClass::Signals | default_flags); - } else { - result = cppClass->functionsInTargetLang(); - } - - return result; -} - void BoostPythonGenerator::writeFunctionCall(QTextStream &s, const AbstractMetaFunction* func, uint options) @@ -355,55 +221,6 @@ void BoostPythonGenerator::writeFunctionCall(QTextStream &s, s << ')'; } -AbstractMetaFunctionList BoostPythonGenerator::filterFunctions(const AbstractMetaClass *cppClass) -{ - AbstractMetaFunctionList lst = queryFunctions(cppClass, true); - foreach (AbstractMetaFunction *func, lst) { - //skip signals - if (func->isSignal() || - func->isDestructor() || - (func->isModifiedRemoved() && !func->isAbstract())) { - lst.removeOne(func); - } - } - - //virtual not implemented in current class - AbstractMetaFunctionList virtual_lst = cppClass->queryFunctions(AbstractMetaClass::VirtualFunctions); - foreach (AbstractMetaFunction *func, virtual_lst) { - if ((func->implementingClass() != cppClass) && - !lst.contains(func)) { - lst.append(func); - } - } - - //append global operators - foreach (AbstractMetaFunction *func , queryGlobalOperators(cppClass)) { - if (!lst.contains(func)) - lst.append(func); - } - - return lst; - //return cpp_class->functions(); -} - -CodeSnipList BoostPythonGenerator::getCodeSnips(const AbstractMetaFunction *func) -{ - CodeSnipList result; - const AbstractMetaClass *cppClass = func->implementingClass(); - while (cppClass) { - foreach (FunctionModification mod, func->modifications(cppClass)) { - if (mod.isCodeInjection()) - result << mod.snips; - } - - if (cppClass == cppClass->baseClass()) - break; - cppClass = cppClass->baseClass(); - } - - return result; -} - void BoostPythonGenerator::writeCodeSnips(QTextStream &s, const CodeSnipList &codeSnips, CodeSnip::Position position, @@ -419,7 +236,7 @@ void BoostPythonGenerator::writeCodeSnips(QTextStream &s, QString code; QTextStream tmpStream(&code); - snip.formattedCode(tmpStream, INDENT); + formatCode(tmpStream, snip.code(), INDENT); if (func) replaceTemplateVariables(code, func); @@ -488,7 +305,7 @@ static void dump_function(AbstractMetaFunctionList lst) } -bool BoostPythonGenerator::prepareGeneration(const QMap<QString, QString>&) +bool BoostPythonGenerator::doSetup(const QMap<QString, QString>&) { return true; } diff --git a/boostpythongenerator.h b/generators/boostpython/boostpythongenerator.h index 4ad191b50..14311b2e0 100644 --- a/boostpythongenerator.h +++ b/generators/boostpython/boostpythongenerator.h @@ -24,8 +24,8 @@ #ifndef BOOSTPYTHONGENERATOR_H #define BOOSTPYTHONGENERATOR_H -#include <apiextractor/generator.h> #include <QtCore/QTextStream> +#include "generator.h" class DocParser; @@ -36,16 +36,6 @@ class BoostPythonGenerator : public Generator { public: /** - * Translate metatypes to boost::python format. - * \param boost_type a pointer to metatype - * \param context the current meta class - * \param option some extra options - * \return the metatype translated to boost::python format - */ - virtual QString translateType(const AbstractMetaType *boost_type, - const AbstractMetaClass *context, - int option = NoOption) const; - /** * Write a function argument in the boost::python format in the text stream \p s. * This function just call \code s << argumentString(); \endcode * \param s text stream used to write the output. @@ -96,8 +86,6 @@ public: CodeSnip::Position position, TypeSystem::Language language, const AbstractMetaFunction *cpp_function = 0); - /// returns the code snips of a function - CodeSnipList getCodeSnips(const AbstractMetaFunction *func); static bool canCreateWrapperFor(const AbstractMetaClass* cppClass); /** * Function witch parse the metafunction information @@ -127,19 +115,13 @@ public: static QString getWrapperName(const AbstractMetaClass* clazz); - virtual bool prepareGeneration(const QMap<QString, QString>& args); + virtual bool doSetup(const QMap<QString, QString>& args); protected: // verify if the class is copyalbe bool isCopyable(const AbstractMetaClass *cpp_class); - static FunctionModificationList functionModifications(const AbstractMetaFunction *meta_function); - AbstractMetaFunctionList queryFunctions(const AbstractMetaClass *cpp_class, bool all_function = false); void writeFunctionCall(QTextStream &s, const AbstractMetaFunction *cpp_func, uint options = 0); - - AbstractMetaFunctionList filterFunctions(const AbstractMetaClass *cpp_class); - AbstractMetaFunctionList queryGlobalOperators(const AbstractMetaClass *cpp_class); - AbstractMetaFunctionList sortContructor(AbstractMetaFunctionList list); }; diff --git a/convertergenerator.cpp b/generators/boostpython/convertergenerator.cpp index ea52b9193..ea52b9193 100644 --- a/convertergenerator.cpp +++ b/generators/boostpython/convertergenerator.cpp diff --git a/convertergenerator.h b/generators/boostpython/convertergenerator.h index 8f91377c0..8f91377c0 100644 --- a/convertergenerator.h +++ b/generators/boostpython/convertergenerator.h diff --git a/cppgenerator.cpp b/generators/boostpython/cppgenerator.cpp index d99815776..ca4b704ff 100644 --- a/cppgenerator.cpp +++ b/generators/boostpython/cppgenerator.cpp @@ -244,18 +244,27 @@ QString CppGenerator::writeFunctionCast(QTextStream &s, QString CppGenerator::verifyDefaultReturnPolicy(const AbstractMetaFunction *cppFunction, const QString& callPolicy) { AbstractMetaType *type = cppFunction->type(); + + //If return type replaced, the return policy need be set manually. + if (!type || !cppFunction->typeReplaced(0).isEmpty()) + return QString(); + + //avoid natives types + if (!type->name().startsWith("Q")) + return QString(); + QString returnPolicy; - if (type && type->isReference() && type->isConstant()) { + if (type->isConstant() && type->isReference()) { returnPolicy = "python::return_value_policy<python::copy_const_reference"; if (!callPolicy.isEmpty()) returnPolicy += ", " + callPolicy; returnPolicy += " >()"; - } else if (type && (type->isReference() || type->isQObject() || type->isObject())) { + } else if (type->isReference() || type->isQObject() || type->isObject() || type->isNativePointer()) { bool cppOwnership = type->isConstant(); if (cppFunction->isStatic() || cppOwnership) { - returnPolicy = "python::return_value_policy<PySide::return_ptr_object< " - + (cppOwnership ? QString("true") : QString("false")) + "> >()"; + returnPolicy = QString("python::return_value_policy<PySide::return_ptr_object<") + + (cppOwnership ? "true" : "false") + QString("> >()"); } else if (type->isQObject() || type->isObject()) { returnPolicy = QString("PySide::return_object<1, 0, %1, %2 %3 %4 >()") .arg(getArgumentType(cppFunction->ownerClass(), cppFunction, -1)) @@ -624,11 +633,13 @@ void CppGenerator::writeModifiedConstructorImpl ( QTextStream& s, const Abstract void CppGenerator::writeConstructorImpl(QTextStream& s, const AbstractMetaFunction* func) { - s << functionSignature(func, getWrapperName(func->ownerClass()) + "::", "", - (Option)(OriginalTypeDescription | SkipDefaultValues)); - s << " : "; + QString wrapperName = getWrapperName(func->ownerClass()); + s << wrapperName << "::" << wrapperName << "(PyObject *py_self" << (func->arguments().size() ? ", " : ""); + writeFunctionArguments(s, func, OriginalTypeDescription | SkipDefaultValues); + s << ")" << endl; + s << INDENT << " : "; writeFunctionCall(s, func); - s << " {" << endl; + s << ", wrapper(py_self)" << endl << "{" << endl; writeCodeSnips(s, getCodeSnips(func), CodeSnip::Beginning, TypeSystem::All, func); writeCodeSnips(s, getCodeSnips(func), CodeSnip::End, TypeSystem::All, func); s << '}' << endl << endl; @@ -644,7 +655,7 @@ void CppGenerator::writeVirtualMethodImplHead(QTextStream& s, const AbstractMeta CodeSnip::Beginning, TypeSystem::NativeCode, func); } - s << INDENT << "python::object method = PySide::detail::get_override(this, \"" << func->implementingClass()->name(); + s << INDENT << "python::object method = get_override(\"" << func->implementingClass()->name(); if (func->implementingClass()->typeEntry()->isObject() || func->implementingClass()->typeEntry()->isQObject()) s << '*'; @@ -682,9 +693,7 @@ void CppGenerator::writeVirtualMethodImplHead(QTextStream& s, const AbstractMeta (func->type()->isObject() || func->type()->isQObject())) { s << INDENT << "PySide::qptr<" << QString(typeName).replace("*", "") << " > __ptr(__result.ptr());" << endl - << INDENT << "if (__ptr.is_wrapper()) {" << endl - << INDENT << INDENT << "python::incref(__result.ptr());" << endl - << INDENT << "}" << endl + << INDENT << "python::incref(__result.ptr());" << endl << INDENT << "__ptr.release_ownership();" << endl; } @@ -902,11 +911,8 @@ void CppGenerator::writeBoostDeclaration(QTextStream& s, const AbstractMetaClass s << INDENT << "python::scope " << wrapperName << "_scope(python_cls);" << endl; if (cppClass->templateBaseClass() && cppClass->templateBaseClass()->typeEntry()->isContainer()) { - //const ContainerTypeEntry *type = static_cast<const ContainerTypeEntry*>(cppClass->templateBaseClass()->typeEntry()); - //if (type->type() == ContainerTypeEntry::ListContainer) { s << endl << INDENT << "//Index suite for QContainer" << endl << INDENT << "python_cls.def(qcontainer_indexing_suite< " << cppClass->qualifiedCppName() << " >());" << endl << endl; - //} } if (isCopyable(cppClass) && !cppClass->isNamespace()) { @@ -1399,9 +1405,9 @@ void CppGenerator::writeGlobalFunctions() if (moduleEntry && moduleEntry->codeSnips().size() > 0) { foreach (CodeSnip snip, moduleEntry->codeSnips()) { if (snip.position == CodeSnip().Beginning) - snip.formattedCode(s, INDENT); + formatCode(s, snip.code(), INDENT); else - snip.formattedCode(snipEnd, INDENT); + formatCode(snipEnd, snip.code(), INDENT); } } @@ -1419,3 +1425,15 @@ void CppGenerator::writeGlobalFunctions() s << "}\n"; } +QMap<QString, QString> CppGenerator::options() const +{ + QMap<QString, QString> res; + res.insert("disable-named-arg", "Disable Python names arguments."); + return res; +} + +bool CppGenerator::doSetup(const QMap<QString, QString>& args ) +{ + m_disableNamedArgs = args.contains("disable-named-arg"); + return BoostPythonGenerator::doSetup(args); +} diff --git a/cppgenerator.h b/generators/boostpython/cppgenerator.h index 03d37438a..5fcc6f38f 100644 --- a/cppgenerator.h +++ b/generators/boostpython/cppgenerator.h @@ -32,16 +32,14 @@ class CppGenerator : public BoostPythonGenerator { public: - void setDisableNamedArgs(bool disable) - { - m_disableNamedArgs = disable; - } - const char* name() const { return "CppGenerator"; } + QMap<QString, QString> options() const; + bool doSetup(const QMap<QString, QString>& args); + protected: QString fileNameForClass(const AbstractMetaClass *cppClass) const; void generateClass(QTextStream &s, const AbstractMetaClass *cppClass); diff --git a/hppgenerator.cpp b/generators/boostpython/hppgenerator.cpp index 4e7da1120..f6d576d08 100644 --- a/hppgenerator.cpp +++ b/generators/boostpython/hppgenerator.cpp @@ -38,8 +38,8 @@ QString HppGenerator::fileNameForClass(const AbstractMetaClass *cppClass) const void HppGenerator::writeCopyCtor(QTextStream &s, const AbstractMetaClass *cppClass) { - s << INDENT << getWrapperName(cppClass) << "(const " << cppClass->qualifiedCppName() << "& self)" - << " : " << cppClass->qualifiedCppName() << "(self)" << endl + s << INDENT << getWrapperName(cppClass) << "(PyObject *py_self, const " << cppClass->qualifiedCppName() << "& self)" + << " : " << cppClass->qualifiedCppName() << "(self), wrapper(py_self)" << endl << INDENT << "{" << endl << INDENT << "}" << endl; } @@ -67,11 +67,13 @@ void HppGenerator::generateClass(QTextStream &s, const AbstractMetaClass *cppCla if (!cppClass->isPolymorphic() || cppClass->hasPrivateDestructor() || cppClass->isNamespace()) s << "namespace " << wrapperName << " {" << endl << endl; + bool needWriteBackReference = false; if (cppClass->isNamespace()) { s << INDENT << "struct Namespace {};" << endl; } else { QString className; bool create_wrapper = canCreateWrapperFor(cppClass); + bool is_wrapper = false; // detect the held type QString held_type = cppClass->typeEntry()->heldTypeValue(); if (held_type.isEmpty() && create_wrapper) @@ -81,16 +83,10 @@ void HppGenerator::generateClass(QTextStream &s, const AbstractMetaClass *cppCla CodeSnip::Declaration, TypeSystem::NativeCode); if (cppClass->isPolymorphic() && !cppClass->hasPrivateDestructor()) { - if (!held_type.isEmpty()) { - s << "// held type forward decalration" << endl; - s << "template<typename T> class " << held_type << ';' << endl; - } - // Class s << "class PYSIDE_LOCAL " << wrapperName; if (create_wrapper) { - s << " : public " << cppClass->qualifiedCppName() << ", public boost::python::wrapper<"; - s << cppClass->qualifiedCppName() << '>'; + s << " : public " << cppClass->qualifiedCppName() << ", public PySide::wrapper"; } s << endl; s << "{" << endl; @@ -101,22 +97,20 @@ void HppGenerator::generateClass(QTextStream &s, const AbstractMetaClass *cppCla if (cppClass->isPolymorphic() && !cppClass->hasPrivateDestructor()) { s << endl << "private:" << endl; - - if (cppClass->hasPrivateDestructor()) - className = cppClass->qualifiedCppName(); - else - className = wrapperName; + className = wrapperName; + is_wrapper = true; } else { className = cppClass->qualifiedCppName(); } // print the huge boost::python::class_ typedef - s << INDENT << "typedef boost::python::class_< " << className; + s << INDENT << "typedef boost::python::class_< " << cppClass->qualifiedCppName(); writeBaseClass(s, cppClass); if (!held_type.isEmpty()) - s << ", PySide::" << held_type << " < " << className << ", PySide::qptr_base::avoid_cache > "; + s << ", PySide::" << held_type << " < " << className << ", qptr_base::no_check_cache | qptr_base::" + << ( is_wrapper ? "wrapper_pointer" : "no_wrapper_pointer") << "> "; if (!isCopyable(cppClass)) s << ", boost::noncopyable"; @@ -143,7 +137,6 @@ void HppGenerator::generateClass(QTextStream &s, const AbstractMetaClass *cppCla writeCodeSnips(s, cppClass->typeEntry()->codeSnips(), CodeSnip::End, TypeSystem::ShellDeclaration); - } QString staticKeyword = cppClass->isNamespace() ? QLatin1String("") : QLatin1String("static "); @@ -159,6 +152,7 @@ void HppGenerator::generateClass(QTextStream &s, const AbstractMetaClass *cppCla s << "};" << endl << endl; + s << "#endif // __" << wrapperName.toUpper() << "__" << endl << endl; } @@ -179,7 +173,14 @@ void HppGenerator::writeFunction(QTextStream &s, const AbstractMetaFunction* fun s << INDENT << "static " << signatureForDefaultVirtualMethod(func, "", "_default", Generator::SkipName) << ';' << endl; } - s << INDENT << functionSignature(func, "", "", Generator::OriginalTypeDescription | Generator::SkipName); + if (func->isConstructor()) { + s << INDENT << getWrapperName(func->ownerClass()) << "(PyObject *py_self" << (func->arguments().size() ? "," : ""); + writeFunctionArguments(s, func, Generator::OriginalTypeDescription | Generator::SkipName); + s << ")"; + } else { + s << INDENT << functionSignature(func, "", "", Generator::OriginalTypeDescription | Generator::SkipName); + } + if (func->isModifiedRemoved() && func->isAbstract()) writeDefaultImplementation(s, func); else diff --git a/hppgenerator.h b/generators/boostpython/hppgenerator.h index 8e0f5f03b..8e0f5f03b 100644 --- a/hppgenerator.h +++ b/generators/boostpython/hppgenerator.h diff --git a/generators/qtdoc/CMakeLists.txt b/generators/qtdoc/CMakeLists.txt new file mode 100644 index 000000000..fb71784ab --- /dev/null +++ b/generators/qtdoc/CMakeLists.txt @@ -0,0 +1,10 @@ +project(qtdoc_generator) + +set(qtdoc_generator_SRC +qtdocgenerator.cpp +) + +add_library(qtdoc_generator SHARED ${qtdoc_generator_SRC}) +target_link_libraries(qtdoc_generator ${APIEXTRACTOR_LIBRARY} ${QT_QTCORE_LIBRARY} genrunner) + +install(TARGETS qtdoc_generator DESTINATION ${LIB_INSTALL_DIR}) diff --git a/docgenerator.cpp b/generators/qtdoc/qtdocgenerator.cpp index 455615e00..762c8a9b6 100644 --- a/docgenerator.cpp +++ b/generators/qtdoc/qtdocgenerator.cpp @@ -21,7 +21,7 @@ * */ -#include "docgenerator.h" +#include "qtdocgenerator.h" #include <reporthandler.h> #include <qtdocparser.h> #include <algorithm> @@ -65,7 +65,7 @@ QString escape(const QStringRef& strref) } -QtXmlToSphinx::QtXmlToSphinx(DocGenerator* generator, const QString& doc, const QString& context) +QtXmlToSphinx::QtXmlToSphinx(QtDocGenerator* generator, const QString& doc, const QString& context) : m_context(context), m_generator(generator), m_insideBold(false), m_insideItalic(false) { m_handlerMap.insert("heading", &QtXmlToSphinx::handleHeadingTag); @@ -843,12 +843,12 @@ static QString getFuncName(const AbstractMetaFunction *cppFunc) { return result.replace("::", "."); } -QString DocGenerator::fileNameForClass(const AbstractMetaClass *cppClass) const +QString QtDocGenerator::fileNameForClass(const AbstractMetaClass *cppClass) const { return QString("%1.rst").arg(getClassName(cppClass)); } -void DocGenerator::writeFormatedText(QTextStream& s, const Documentation& doc, const AbstractMetaClass* metaClass) +void QtDocGenerator::writeFormatedText(QTextStream& s, const Documentation& doc, const AbstractMetaClass* metaClass) { QString metaClassName; @@ -865,7 +865,7 @@ void DocGenerator::writeFormatedText(QTextStream& s, const Documentation& doc, c s << endl; } -void DocGenerator::writeFunctionBrief(QTextStream &s, +void QtDocGenerator::writeFunctionBrief(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaFunction *cppFunction) { @@ -878,7 +878,7 @@ void DocGenerator::writeFunctionBrief(QTextStream &s, << " (" << parseArgDocStyle(cppClass, cppFunction) << "):"; } -void DocGenerator::generateClass(QTextStream &s, const AbstractMetaClass *cppClass) +void QtDocGenerator::generateClass(QTextStream &s, const AbstractMetaClass *cppClass) { QString doc; QTextStream doc_s(&doc); @@ -898,35 +898,6 @@ void DocGenerator::generateClass(QTextStream &s, const AbstractMetaClass *cppCla AbstractMetaFunctionList functionList = filterFunctions(cppClass); qSort(functionList.begin(), functionList.end(), functionSort); -#if 0 - if (functionList.size() > 0) - { - QtXmlToSphinx::Table functionTable; - QtXmlToSphinx::TableRow row; - - s << "Functions\n" - "---------\n\n"; - - - foreach (AbstractMetaFunction *func, functionList) { - if ((func->isConstructor() || func->isModifiedRemoved()) || - (func->declaringClass() != cppClass)) - continue; - - QString rowString; - QTextStream rowStream(&rowString); - - writeFunctionBrief(rowStream, cppClass, func); - row << rowString; - functionTable << row; - row.clear(); - } - functionTable.normalize(); - s << functionTable; - } - -#endif - doc_s << "Detailed Description\n" "--------------------\n\n"; @@ -961,7 +932,7 @@ void DocGenerator::generateClass(QTextStream &s, const AbstractMetaClass *cppCla s << doc; } -QString DocGenerator::parseFunctionDeclaration(const QString &doc, const AbstractMetaClass *cppClass) +QString QtDocGenerator::parseFunctionDeclaration(const QString &doc, const AbstractMetaClass *cppClass) { //.. method:: QObject.childEvent(arg__1) //def :meth:`removeEventFilter<QObject.removeEventFilter>` (arg__1): @@ -1001,7 +972,7 @@ QString DocGenerator::parseFunctionDeclaration(const QString &doc, const Abstrac } -void DocGenerator::writeFunctionList(QTextStream &s, const QString &content, const AbstractMetaClass *cppClass) +void QtDocGenerator::writeFunctionList(QTextStream &s, const QString &content, const AbstractMetaClass *cppClass) { QStringList functionList; QStringList staticFunctionList; @@ -1058,7 +1029,7 @@ void DocGenerator::writeFunctionList(QTextStream &s, const QString &content, con } } -void DocGenerator::writeEnums(QTextStream& s, const AbstractMetaClass* cppClass) +void QtDocGenerator::writeEnums(QTextStream& s, const AbstractMetaClass* cppClass) { static const QString section_title(".. attribute:: "); @@ -1068,7 +1039,7 @@ void DocGenerator::writeEnums(QTextStream& s, const AbstractMetaClass* cppClass) } } -void DocGenerator::writeFields(QTextStream &s, const AbstractMetaClass *cppClass) +void QtDocGenerator::writeFields(QTextStream &s, const AbstractMetaClass *cppClass) { static const QString section_title(".. attribute:: "); @@ -1079,7 +1050,7 @@ void DocGenerator::writeFields(QTextStream &s, const AbstractMetaClass *cppClass } } -void DocGenerator::writeConstructors(QTextStream &s, const AbstractMetaClass *cppClass) +void QtDocGenerator::writeConstructors(QTextStream &s, const AbstractMetaClass *cppClass) { static const QString sectionTitle = ".. class:: "; static const QString sectionTitleSpace = QString(sectionTitle.size(), ' '); @@ -1122,7 +1093,7 @@ void DocGenerator::writeConstructors(QTextStream &s, const AbstractMetaClass *cp } } -QString DocGenerator::parseArgDocStyle(const AbstractMetaClass *cppClass, const AbstractMetaFunction *func) +QString QtDocGenerator::parseArgDocStyle(const AbstractMetaClass *cppClass, const AbstractMetaFunction *func) { QString ret; bool optional = false; @@ -1152,7 +1123,7 @@ QString DocGenerator::parseArgDocStyle(const AbstractMetaClass *cppClass, const return ret; } -void DocGenerator::writeDocSnips(QTextStream &s, +void QtDocGenerator::writeDocSnips(QTextStream &s, const CodeSnipList &codeSnips, CodeSnip::Position position, TypeSystem::Language language) @@ -1215,7 +1186,7 @@ void DocGenerator::writeDocSnips(QTextStream &s, } } -void DocGenerator::writeInjectDocumentation(QTextStream &s, +void QtDocGenerator::writeInjectDocumentation(QTextStream &s, DocModification::Mode mode, const AbstractMetaClass *cppClass, const AbstractMetaFunction *func) @@ -1257,7 +1228,7 @@ void DocGenerator::writeInjectDocumentation(QTextStream &s, } } -void DocGenerator::writeFunctionSignature(QTextStream& s, const AbstractMetaClass* cppClass, const AbstractMetaFunction* func) +void QtDocGenerator::writeFunctionSignature(QTextStream& s, const AbstractMetaClass* cppClass, const AbstractMetaFunction* func) { if (!func->isConstructor()) s << getClassName(cppClass) << '.'; @@ -1266,7 +1237,7 @@ void DocGenerator::writeFunctionSignature(QTextStream& s, const AbstractMetaClas s << getFuncName(func) << "(" << parseArgDocStyle(cppClass, func) << ")"; } -QString DocGenerator::translateToPythonType(const AbstractMetaType *type, const AbstractMetaClass *cppClass) +QString QtDocGenerator::translateToPythonType(const AbstractMetaType *type, const AbstractMetaClass *cppClass) { QString originalType = translateType(type, cppClass, Generator::ExcludeConst | Generator::ExcludeReference); QString strType = originalType; @@ -1299,13 +1270,13 @@ QString DocGenerator::translateToPythonType(const AbstractMetaType *type, const } } -void DocGenerator::writeParamerteType(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaArgument *arg) +void QtDocGenerator::writeParamerteType(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaArgument *arg) { s << INDENT << ":param " << arg->argumentName() << ": " << translateToPythonType(arg->type(), cppClass) << endl; } -void DocGenerator::writeFunctionParametersType(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaFunction* func) +void QtDocGenerator::writeFunctionParametersType(QTextStream &s, const AbstractMetaClass *cppClass, const AbstractMetaFunction* func) { Indentation indentation(INDENT); @@ -1324,7 +1295,7 @@ void DocGenerator::writeFunctionParametersType(QTextStream &s, const AbstractMet s << endl; } -void DocGenerator::writeFunction(QTextStream &s, bool writeDoc, const AbstractMetaClass *cppClass, const AbstractMetaFunction* func) +void QtDocGenerator::writeFunction(QTextStream &s, bool writeDoc, const AbstractMetaClass *cppClass, const AbstractMetaFunction* func) { writeFunctionSignature(s, cppClass, func); s << endl; @@ -1339,7 +1310,7 @@ void DocGenerator::writeFunction(QTextStream &s, bool writeDoc, const AbstractMe } } -void DocGenerator::finishGeneration() +void QtDocGenerator::finishGeneration() { if (classes().isEmpty()) return; @@ -1383,9 +1354,8 @@ void DocGenerator::finishGeneration() } } -bool DocGenerator::prepareGeneration(const QMap<QString, QString>& args) +bool QtDocGenerator::doSetup(const QMap<QString, QString>& args) { - BoostPythonGenerator::prepareGeneration(args); m_libSourceDir = args.value("library-source-dir"); setOutputDirectory(args.value("documentation-out-dir")); m_docDataDir = args.value("documentation-data-dir"); @@ -1409,7 +1379,7 @@ bool DocGenerator::prepareGeneration(const QMap<QString, QString>& args) } -QMap<QString, QString> DocGenerator::options() const +QMap<QString, QString> QtDocGenerator::options() const { QMap<QString, QString> options; options.insert("library-source-dir", "Directory where library source code is located"); diff --git a/docgenerator.h b/generators/qtdoc/qtdocgenerator.h index 225b429b4..b312d4455 100644 --- a/docgenerator.h +++ b/generators/qtdoc/qtdocgenerator.h @@ -23,11 +23,16 @@ #ifndef DOCGENERATOR_H #define DOCGENERATOR_H -#include "boostpythongenerator.h" #include <QtCore/QStack> +#include <QtCore/QHash> +#include <QtCore/QTextStream> +#include <apiextractor/abstractmetalang.h> +#include "generator.h" +class AbstractMetaFunction; +class AbstractMetaClass; class QXmlStreamReader; -class DocGenerator; +class QtDocGenerator; class QtXmlToSphinx { @@ -77,7 +82,7 @@ public: bool m_normalized; }; - QtXmlToSphinx(DocGenerator* generator, const QString& doc, const QString& context = QString()); + QtXmlToSphinx(QtDocGenerator* generator, const QString& doc, const QString& context = QString()); QString result() const { @@ -128,7 +133,7 @@ private: Table m_currentTable; bool m_tableHasHeader; QString m_context; - DocGenerator* m_generator; + QtDocGenerator* m_generator; bool m_insideBold; bool m_insideItalic; QString m_lastTagName; @@ -150,24 +155,19 @@ QTextStream& operator<<(QTextStream& s, const QtXmlToSphinx::Table &table); /** * The DocGenerator generates documentation from library being binded. */ -class DocGenerator : public BoostPythonGenerator +class QtDocGenerator : public Generator { public: - virtual GeneratorType type() const - { - return DocumentationType; - } - QString libSourceDir() const { return m_libSourceDir; } - virtual bool prepareGeneration(const QMap<QString, QString>& args); + bool doSetup(const QMap<QString, QString>& args); const char* name() const { - return "DocGenerator"; + return "QtDocGenerator"; } QMap<QString, QString> options() const; @@ -181,6 +181,15 @@ protected: QString fileNameForClass(const AbstractMetaClass* cppClass) const; void generateClass(QTextStream& s, const AbstractMetaClass* cppClass); void finishGeneration(); + + void writeFunctionArguments(QTextStream&, const AbstractMetaFunction*, uint) const {} + void writeArgumentNames(QTextStream&, const AbstractMetaFunction*, uint) const {} + QString subDirectoryForClass(const AbstractMetaClass* clazz) const + { + Q_ASSERT(false); + return QString(); + } + private: void writeEnums(QTextStream& s, const AbstractMetaClass* cppClass); @@ -21,32 +21,186 @@ * */ -#include <QtCore/QCoreApplication> +#include <QCoreApplication> +#include <QLinkedList> +#include <QLibrary> +#include <iostream> #include <apiextractor/apiextractor.h> -#include "hppgenerator.h" -#include "cppgenerator.h" -#include "hppgenerator.h" -#include "convertergenerator.h" -#include "docgenerator.h" #include "boostpythongeneratorversion.h" -#include <iostream> +#include "generator.h" -void showVersion(const char* apiextractor_version) { - using namespace std; +#if defined(Q_OS_WIN32) + #define PATH_SPLITTER ";" +#else + #define PATH_SPLITTER ":" +#endif - cout << "BoostPythonGenerator v" BOOSTPYTHONGENERATOR_VERSION << " using " << apiextractor_version << endl; - cout << "Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)" << endl; + +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 QLinkedList<Generator*> (*getGeneratorsFunc)(); + +QMap<QString, QString> getCommandLineArgs(int argc, char** argv) +{ + QMap<QString, QString> args; + int argNum = 0; + for (int i = 1; i < argc; ++i) { + QString arg(argv[i]); + 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 " + << "generator [options] header-file typesystem-file\n\n" + "General options:\n"; + QMap<QString, QString> generalOptions; + 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=[dir]", "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=[licensefile]", "File used for copyright headers of generated files"); + generalOptions.insert("version", "Output version information and exit"); + generalOptions.insert("generatorSet", "generatorSet to be used. e.g. boostpython"); + 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()); + } + } } int main(int argc, char *argv[]) { - QCoreApplication app(argc, argv); // needed by qxmlpatterns - - ApiExtractor extractor(argc, argv); - extractor.addGenerator(new HppGenerator); - extractor.addGenerator(new CppGenerator); - extractor.addGenerator(new ConverterGenerator); - extractor.addGenerator(new DocGenerator); - extractor.setVersionHandler(&showVersion); - return extractor.exec(); + // needed by qxmlpatterns + QCoreApplication app(argc, argv); + + // Store command arguments in a map + QMap<QString, QString> args = getCommandLineArgs(argc, argv); + GeneratorList generators; + + if (args.contains("version")) { + std::cout << "generator v" BOOSTPYTHONGENERATOR_VERSION << std::endl; + std::cout << "Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)" << std::endl; + return EXIT_SUCCESS; + } + + // Try to load a generator + QString generatorSet = args.value("generatorSet"); + if (!generatorSet.isEmpty()) { + QLibrary plugin(generatorSet+"_generator"); + getGeneratorsFunc getGenerators = reinterpret_cast<getGeneratorsFunc>(plugin.resolve("getGenerators")); + if (getGenerators) + generators = getGenerators(); + else { + std::cerr << argv[0] << ": Error loading generatorset plugin: " << qPrintable(plugin.errorString()) << std::endl; + return EXIT_FAILURE; + } + } else if (!args.contains("help")) { + std::cerr << argv[0] << ": You need to specify a generator with --generatorSet=GENERATOR_NAME" << std::endl; + return EXIT_FAILURE; + } + + if (args.contains("help")) { + printUsage(generators); + return EXIT_SUCCESS; + } + + + QString licenseComment; + if (args.contains("license-file") && !args.value("license-file").isEmpty()) { + QString licenseFileName = args.value("license-file"); + if (QFile::exists(licenseFileName)) { + QFile licenseFile(licenseFileName); + if (licenseFile.open(QIODevice::ReadOnly)) + licenseComment = licenseFile.readAll(); + } else { + std::cerr << "Couldn't find the file containing the license heading: "; + std::cerr << qPrintable(licenseFileName) << std::endl; + return EXIT_FAILURE; + } + } + + // Create and set-up API Extractor + ApiExtractor extractor; + + if (args.contains("silent")) { + extractor.setSilent(true); + } else if (args.contains("debug-level")) { + QString level = args.value("debug-level"); + if (level == "sparse") + extractor.setDebugLevel(ReportHandler::SparseDebug); + else if (level == "medium") + extractor.setDebugLevel(ReportHandler::MediumDebug); + else if (level == "full") + extractor.setDebugLevel(ReportHandler::FullDebug); + } + if (args.contains("no-suppress-warnings")) + extractor.setSuppressWarnings(false); + + if (args.contains("typesystem-paths")) + extractor.addTypesystemSearchPath(args.value("typesystem-paths").split(PATH_SPLITTER)); + if (!args.value("include-paths").isEmpty()) + extractor.addIncludePath(args.value("include-paths").split(PATH_SPLITTER)); + + + QString cppFileName = args.value("arg-1"); + QString typeSystemFileName = args.value("arg-2"); + if (args.contains("arg-3")) { + std::cerr << "Too many arguments!" << std::endl; + return EXIT_FAILURE; + } + extractor.setCppFileName(cppFileName); + extractor.setTypeSystem(typeSystemFileName); + extractor.run(); + + if (!extractor.classCount()) { + std::cerr << "No C++ classes found!" << std::endl; + return EXIT_FAILURE; + } + + + QString outputDirectory = args.contains("output-directory") ? args["output-directory"] : "out"; + foreach (Generator* g, generators) { + g->setOutputDirectory(outputDirectory); + g->setLicenseComment(licenseComment); + if (g->setup(extractor, args)) + g->generate(); + } + + std::cout << "Done, " << ReportHandler::warningCount(); + std::cout << " warnings (" << ReportHandler::suppressedCount() << " known issues)"; + std::cout << std::endl; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9099adff8..b4fba105d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -3,7 +3,7 @@ project(sphinxtabletest) # TODO set(sphinxtabletest_SRC sphinxtabletest.cpp) qt4_automoc(${sphinxtabletest_SRC}) -include_directories(${QT_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${boostpythongenerator_SOURCE_DIR}) +include_directories(${QT_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${qtdoc_generator_SOURCE_DIR}) add_executable(sphinxtabletest ${sphinxtabletest_SRC}) -target_link_libraries(sphinxtabletest ${QT_QTTEST_LIBRARY} ${APIEXTRACTOR_LIBRARY} libboostpythongenerator) +target_link_libraries(sphinxtabletest ${QT_QTTEST_LIBRARY} ${APIEXTRACTOR_LIBRARY} qtdoc_generator genrunner) add_test("sphinxtable" sphinxtabletest) diff --git a/tests/sphinxtabletest.cpp b/tests/sphinxtabletest.cpp index 058a3b522..a35d11926 100644 --- a/tests/sphinxtabletest.cpp +++ b/tests/sphinxtabletest.cpp @@ -22,7 +22,7 @@ */ #include "sphinxtabletest.h" -#include "docgenerator.h" +#include "qtdocgenerator.h" #include <QtTest/QTest> #include <QDebug> @@ -33,7 +33,7 @@ QString SphinxTableTest::transformXml(const char* xml) void SphinxTableTest::setUp() { - m_generator = new DocGenerator; + m_generator = new QtDocGenerator; } void SphinxTableTest::tearDown() diff --git a/tests/sphinxtabletest.h b/tests/sphinxtabletest.h index 163cc5337..57d8937d0 100644 --- a/tests/sphinxtabletest.h +++ b/tests/sphinxtabletest.h @@ -26,7 +26,7 @@ #include <QObject> -class DocGenerator; +class QtDocGenerator; class SphinxTableTest : public QObject { Q_OBJECT @@ -40,7 +40,7 @@ private slots: void testComplexTable(); void testRowSpan2(); private: - DocGenerator* m_generator; + QtDocGenerator* m_generator; QString transformXml(const char* xml); }; |