From e336872828de86146059de0f62ed06afaaf64d8d Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 25 Apr 2019 09:12:36 +0200 Subject: shiboken: Output module name in "done" message Move the formatting of the "done" message to the report handler and add the prefix, which is the module name. Change-Id: I63aa1f48f02709d6e89d9a9a684d56a218e65fd3 Reviewed-by: Cristian Maureira-Fredes --- sources/shiboken2/ApiExtractor/reporthandler.cpp | 17 +++++++++++++++++ sources/shiboken2/ApiExtractor/reporthandler.h | 2 ++ sources/shiboken2/generator/main.cpp | 11 ++--------- 3 files changed, 21 insertions(+), 9 deletions(-) (limited to 'sources/shiboken2') diff --git a/sources/shiboken2/ApiExtractor/reporthandler.cpp b/sources/shiboken2/ApiExtractor/reporthandler.cpp index 36f725e30..6ff5b8d03 100644 --- a/sources/shiboken2/ApiExtractor/reporthandler.cpp +++ b/sources/shiboken2/ApiExtractor/reporthandler.cpp @@ -29,6 +29,7 @@ #include "reporthandler.h" #include "typesystem.h" #include "typedatabase.h" +#include #include #include #include @@ -56,6 +57,7 @@ static QString m_prefix; static int m_step_size = 0; static int m_step = -1; static int m_step_warning = 0; +static QElapsedTimer m_timer; Q_LOGGING_CATEGORY(lcShiboken, "qt.shiboken") @@ -69,6 +71,7 @@ static void printProgress() void ReportHandler::install() { qInstallMessageHandler(ReportHandler::messageOutput); + m_timer.start(); } ReportHandler::DebugLevel ReportHandler::debugLevel() @@ -157,3 +160,17 @@ void ReportHandler::progress(const QString& str, ...) m_step_warning = 0; } } + +QByteArray ReportHandler::doneMessage() +{ + QByteArray result = "Done, " + m_prefix.toUtf8() + ' '; + const qint64 elapsed = m_timer.elapsed(); + result += elapsed > 5000 + ? QByteArray::number(elapsed / 1000) + 's' + : QByteArray::number(elapsed) + "ms"; + if (m_warningCount) + result += ", " + QByteArray::number(m_warningCount) + " warnings"; + if (m_suppressedCount) + result += " (" + QByteArray::number(m_suppressedCount) + " known issues)"; + return result; +} diff --git a/sources/shiboken2/ApiExtractor/reporthandler.h b/sources/shiboken2/ApiExtractor/reporthandler.h index c122f005d..8f97cb506 100644 --- a/sources/shiboken2/ApiExtractor/reporthandler.h +++ b/sources/shiboken2/ApiExtractor/reporthandler.h @@ -66,6 +66,8 @@ public: static void setPrefix(const QString &p); + static QByteArray doneMessage(); + private: static void messageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg); }; diff --git a/sources/shiboken2/generator/main.cpp b/sources/shiboken2/generator/main.cpp index 1817f6b03..9beaf47c7 100644 --- a/sources/shiboken2/generator/main.cpp +++ b/sources/shiboken2/generator/main.cpp @@ -27,7 +27,6 @@ ****************************************************************************/ #include -#include #include #include #include @@ -385,8 +384,6 @@ int main(int argc, char *argv[]) // PYSIDE-757: Request a deterministic ordering of QHash in the code model // and type system. qSetGlobalQHashSeed(0); - QElapsedTimer timer; - timer.start(); // needed by qxmlpatterns QCoreApplication app(argc, argv); ReportHandler::install(); @@ -634,12 +631,8 @@ int main(int argc, char *argv[]) } } - 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; + const QByteArray doneMessage = ReportHandler::doneMessage(); + qCDebug(lcShiboken, "%s", doneMessage.constData()); std::cout << doneMessage.constData() << std::endl; return EXIT_SUCCESS; -- cgit v1.2.3 From 21077bec48f2ca16ec0fed25e0c0af62317606d9 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 18 Apr 2019 12:41:14 +0200 Subject: shiboken2: Enable documentation generation with libxml2/libxslt As QtXmlPatterns is deprecated in Qt 5.14, the documentation build needs to be changed to work with libxml2/libxslt exclusively. Split the XML functionality into separate files for libxslt and Qt and provide an interface for XPATH queries and XSLT transformations in xmlutils.h. Adapt testmodifydocumentation to work on temporary files as libxslt cannot handle Qt resources. Change-Id: I923f5b2e7c1d2511f15788e4b80c7721daeb2bc3 Reviewed-by: Cristian Maureira-Fredes --- sources/shiboken2/ApiExtractor/CMakeLists.txt | 26 ++- sources/shiboken2/ApiExtractor/docparser.cpp | 183 +++------------- sources/shiboken2/ApiExtractor/docparser.h | 17 +- sources/shiboken2/ApiExtractor/doxygenparser.cpp | 19 +- sources/shiboken2/ApiExtractor/qtdocparser.cpp | 21 +- sources/shiboken2/ApiExtractor/qtdocparser.h | 2 +- .../ApiExtractor/tests/testmodifydocumentation.cpp | 11 +- sources/shiboken2/ApiExtractor/xmlutils.cpp | 68 ++++++ sources/shiboken2/ApiExtractor/xmlutils.h | 53 +++++ .../shiboken2/ApiExtractor/xmlutils_libxslt.cpp | 231 +++++++++++++++++++++ sources/shiboken2/ApiExtractor/xmlutils_libxslt.h | 40 ++++ sources/shiboken2/ApiExtractor/xmlutils_qt.cpp | 102 +++++++++ sources/shiboken2/ApiExtractor/xmlutils_qt.h | 40 ++++ sources/shiboken2/CMakeLists.txt | 7 +- sources/shiboken2/generator/CMakeLists.txt | 1 - 15 files changed, 631 insertions(+), 190 deletions(-) create mode 100644 sources/shiboken2/ApiExtractor/xmlutils.cpp create mode 100644 sources/shiboken2/ApiExtractor/xmlutils.h create mode 100644 sources/shiboken2/ApiExtractor/xmlutils_libxslt.cpp create mode 100644 sources/shiboken2/ApiExtractor/xmlutils_libxslt.h create mode 100644 sources/shiboken2/ApiExtractor/xmlutils_qt.cpp create mode 100644 sources/shiboken2/ApiExtractor/xmlutils_qt.h (limited to 'sources/shiboken2') diff --git a/sources/shiboken2/ApiExtractor/CMakeLists.txt b/sources/shiboken2/ApiExtractor/CMakeLists.txt index 147fda377..760cc6985 100644 --- a/sources/shiboken2/ApiExtractor/CMakeLists.txt +++ b/sources/shiboken2/ApiExtractor/CMakeLists.txt @@ -26,6 +26,7 @@ clangparser/clangutils.cpp # Old parser parser/codemodel.cpp parser/enumvalue.cpp +xmlutils.cpp ) add_library(apiextractor STATIC ${apiextractor_SRC}) @@ -37,19 +38,26 @@ target_include_directories(apiextractor PRIVATE ${CLANG_EXTRA_INCLUDES} target_link_libraries(apiextractor PUBLIC Qt5::Core) target_link_libraries(apiextractor PRIVATE ${CLANG_EXTRA_LIBRARIES}) +if (HAS_LIBXSLT) + target_compile_definitions(apiextractor PUBLIC HAVE_LIBXSLT) + target_sources(apiextractor PRIVATE xmlutils_libxslt.cpp) + target_include_directories(apiextractor + PRIVATE ${LIBXSLT_INCLUDE_DIR} ${LIBXML2_INCLUDE_DIR}) + target_link_libraries(apiextractor + PRIVATE ${LIBXSLT_LIBRARIES} ${LIBXML2_LIBRARIES}) +endif() + +if (Qt5XmlPatterns_FOUND) + target_compile_definitions(apiextractor PUBLIC HAVE_QTXMLPATTERNS) + target_sources(apiextractor PRIVATE xmlutils_qt.cpp) + target_link_libraries(apiextractor PUBLIC Qt5::Xml Qt5::XmlPatterns) +endif() + if (NOT DISABLE_DOCSTRINGS) target_sources(apiextractor PRIVATE docparser.cpp doxygenparser.cpp qtdocparser.cpp) - target_link_libraries(apiextractor PUBLIC Qt5::Xml Qt5::XmlPatterns) - - if (LIBXSLT_FOUND AND LIBXML2_FOUND) - target_compile_definitions(apiextractor PUBLIC HAVE_LIBXSLT) - target_include_directories(apiextractor - PRIVATE ${LIBXSLT_INCLUDE_DIR} ${LIBXML2_INCLUDE_DIR}) - target_link_libraries(apiextractor - PRIVATE ${LIBXSLT_LIBRARIES} ${LIBXML2_LIBRARIES}) - else() + if (NOT HAS_LIBXSLT) message(WARNING "libxslt and/or libxml not found, falling back to QtXmlPatterns (QTBUG-66925)") endif() diff --git a/sources/shiboken2/ApiExtractor/docparser.cpp b/sources/shiboken2/ApiExtractor/docparser.cpp index 99921e7d3..532956d1a 100644 --- a/sources/shiboken2/ApiExtractor/docparser.cpp +++ b/sources/shiboken2/ApiExtractor/docparser.cpp @@ -30,10 +30,10 @@ #include "messages.h" #include "reporthandler.h" #include "typesystem.h" +#include "xmlutils.h" #include #include #include -#include #include #include @@ -53,27 +53,19 @@ DocParser::DocParser() DocParser::~DocParser() = default; -QString DocParser::getDocumentation(QXmlQuery& xquery, const QString& query, +QString DocParser::getDocumentation(const XQueryPtr &xquery, const QString& query, const DocModificationList& mods) const { QString doc = execXQuery(xquery, query); return applyDocModifications(mods, doc.trimmed()); } -QString DocParser::execXQuery(QXmlQuery& xquery, const QString& query) const +QString DocParser::execXQuery(const XQueryPtr &xquery, const QString& query) const { - QString escapedQuery(query); - // XQuery can't have invalid XML characters - escapedQuery.replace(QLatin1Char('&'), QLatin1String("&")); - escapedQuery.replace(QLatin1Char('<'), QLatin1String("<")); - xquery.setQuery(escapedQuery); - if (!xquery.isValid()) { - qWarning() << "Bad XQuery: " << escapedQuery; - return QString(); - } - - QString result; - xquery.evaluateTo(&result); + QString errorMessage; + const QString result = xquery->evaluate(query, &errorMessage); + if (!errorMessage.isEmpty()) + qCWarning(lcShiboken, "%s", qPrintable(errorMessage)); return result; } @@ -108,41 +100,6 @@ AbstractMetaFunctionList DocParser::documentableFunctions(const AbstractMetaClas return result; } - -#ifdef HAVE_LIBXSLT -namespace -{ - -class XslResources -{ - Q_DISABLE_COPY(XslResources) - -public: - xmlDocPtr xmlDoc = nullptr; - xsltStylesheetPtr xslt = nullptr; - xmlDocPtr xslResult = nullptr; - - XslResources() = default; - - ~XslResources() - { - if (xslt) - xsltFreeStylesheet(xslt); - - if (xslResult) - xmlFreeDoc(xslResult); - - if (xmlDoc) - xmlFreeDoc(xmlDoc); - - xsltCleanupGlobals(); - xmlCleanupParser(); - } -}; - -} // namespace -#endif // HAVE_LIBXSLT - static inline bool isXpathDocModification(const DocModification &mod) { return mod.mode() == TypeSystem::DocModificationXPathReplace; @@ -150,102 +107,22 @@ static inline bool isXpathDocModification(const DocModification &mod) QString DocParser::applyDocModifications(const DocModificationList& mods, const QString& xml) const { + const char xslPrefix[] = +R"( + + + + + + + + +)"; + if (mods.isEmpty() || xml.isEmpty() || !std::any_of(mods.cbegin(), mods.cend(), isXpathDocModification)) { return xml; } -#ifdef HAVE_LIBXSLT - const QString result = applyDocModificationsLibXsl(mods, xml); -#else - const QString result = applyDocModificationsQt(mods, xml); -#endif - if (result == xml) { - const QString message = QLatin1String("Query did not result in any modifications to \"") - + xml + QLatin1Char('"'); - qCWarning(lcShiboken, "%s", - qPrintable(msgXpathDocModificationError(mods, message))); - } - return result; -} - -QString DocParser::applyDocModificationsLibXsl(const DocModificationList& mods, const QString& xml) const -{ -#ifdef HAVE_LIBXSLT - QString xsl = QLatin1String("\n" - "\n" - "\n" - " \n" - "\n" - "\n" - "\n" - " \n" - " \n" - "\n" - "\n" - ); - for (const DocModification &mod : mods) { - if (isXpathDocModification(mod)) { - QString xpath = mod.xpath(); - xpath.replace(QLatin1Char('"'), QLatin1String(""")); - xsl += QLatin1String("") - + mod.code() + QLatin1String("\n"); - } - } - xsl += QLatin1String(""); - - XslResources res; - // Read XML data - QByteArray xmlData = xml.toUtf8(); - res.xmlDoc = xmlParseMemory(xmlData.constData(), xmlData.size()); - if (!res.xmlDoc) - return xml; - - // Read XSL data as a XML file - QByteArray xslData = xsl.toUtf8(); - // xsltFreeStylesheet will delete this pointer - xmlDocPtr xslDoc = xmlParseMemory(xslData.constData(), xslData.size()); - if (!xslDoc) - return xml; - - // Parse XSL data - res.xslt = xsltParseStylesheetDoc(xslDoc); - if (!res.xslt) - return xml; - - // Apply XSL - res.xslResult = xsltApplyStylesheet(res.xslt, res.xmlDoc, 0); - xmlChar* buffer = 0; - int bufferSize; - QString result; - if (!xsltSaveResultToString(&buffer, &bufferSize, res.xslResult, res.xslt)) { - result = QString::fromUtf8(reinterpret_cast(buffer), bufferSize); - std::free(buffer); - } else { - result = xml; - } - return result.trimmed(); -#else // HAVE_LIBXSLT - Q_UNUSED(mods) - return xml; -#endif // !HAVE_LIBXSLT -} - -QString DocParser::applyDocModificationsQt(const DocModificationList& mods, const QString& xml) const -{ - const char xslPrefix[] = -R"( - - - \n" - - - - - - - -)"; QString xsl = QLatin1String(xslPrefix); for (const DocModification &mod : mods) { @@ -257,21 +134,17 @@ R"( + mod.code() + QLatin1String("\n"); } } - xsl += QLatin1String(""); - QXmlQuery query(QXmlQuery::XSLT20); - query.setFocus(xml); - query.setQuery(xsl); - if (!query.isValid()) { + QString errorMessage; + const QString result = xsl_transform(xml, xsl, &errorMessage); + if (!errorMessage.isEmpty()) qCWarning(lcShiboken, "%s", - qPrintable(msgXpathDocModificationError(mods, QLatin1String("Invalid query.")))); - return xml; - } - QString result; - if (!query.evaluateTo(&result)) { + qPrintable(msgXpathDocModificationError(mods, errorMessage))); + if (result == xml) { + const QString message = QLatin1String("Query did not result in any modifications to \"") + + xml + QLatin1Char('"'); qCWarning(lcShiboken, "%s", - qPrintable(msgXpathDocModificationError(mods, QLatin1String("evaluate() failed.")))); - return xml; + qPrintable(msgXpathDocModificationError(mods, message))); } - return result.trimmed(); + return result; } diff --git a/sources/shiboken2/ApiExtractor/docparser.h b/sources/shiboken2/ApiExtractor/docparser.h index 7cbbef28e..53507b0f5 100644 --- a/sources/shiboken2/ApiExtractor/docparser.h +++ b/sources/shiboken2/ApiExtractor/docparser.h @@ -32,20 +32,19 @@ #include "abstractmetalang_typedefs.h" #include - -QT_BEGIN_NAMESPACE -class QDomDocument; -class QDomNode; -class QXmlQuery; -QT_END_NAMESPACE +#include class AbstractMetaClass; class DocModification; class Documentation; +class XQuery; + class DocParser { public: + using XQueryPtr = QSharedPointer; + DocParser(); virtual ~DocParser(); virtual void fillDocumentation(AbstractMetaClass* metaClass) = 0; @@ -114,7 +113,7 @@ public: static bool skipForQuery(const AbstractMetaFunction *func); protected: - QString getDocumentation(QXmlQuery& xquery, const QString& query, + QString getDocumentation(const XQueryPtr &xquery, const QString& query, const DocModificationList& mods) const; @@ -125,10 +124,8 @@ private: QString m_docDataDir; QString m_libSourceDir; - QString execXQuery(QXmlQuery& xquery, const QString& query) const; + QString execXQuery(const XQueryPtr &xquery, const QString& query) const; QString applyDocModifications(const DocModificationList& mods, const QString& xml) const; - QString applyDocModificationsLibXsl(const DocModificationList& mods, const QString& xml) const; - QString applyDocModificationsQt(const DocModificationList& mods, const QString& xml) const; }; #endif // DOCPARSER_H diff --git a/sources/shiboken2/ApiExtractor/doxygenparser.cpp b/sources/shiboken2/ApiExtractor/doxygenparser.cpp index e238aa1f7..94c9ec7e0 100644 --- a/sources/shiboken2/ApiExtractor/doxygenparser.cpp +++ b/sources/shiboken2/ApiExtractor/doxygenparser.cpp @@ -31,8 +31,8 @@ #include "messages.h" #include "reporthandler.h" #include "typesystem.h" +#include "xmlutils.h" -#include #include #include @@ -86,8 +86,13 @@ void DoxygenParser::fillDocumentation(AbstractMetaClass* metaClass) << "/{struct|class|namespace}"<< doxyFileSuffix; return; } - QXmlQuery xquery; - xquery.setFocus(QUrl(doxyFilePath)); + + QString errorMessage; + XQueryPtr xquery = XQuery::create(doxyFilePath, &errorMessage); + if (xquery.isNull()) { + qCWarning(lcShiboken, "%s", qPrintable(errorMessage)); + return; + } // Get class documentation const QString classQuery = QLatin1String("/doxygen/compounddef/detaileddescription"); @@ -190,8 +195,12 @@ Documentation DoxygenParser::retrieveModuleDocumentation(const QString& name){ return Documentation(); } - QXmlQuery xquery; - xquery.setFocus(QUrl(sourceFile)); + QString errorMessage; + XQueryPtr xquery = XQuery::create(sourceFile, &errorMessage); + if (xquery.isNull()) { + qCWarning(lcShiboken, "%s", qPrintable(errorMessage)); + return {}; + } // Module documentation QString query = QLatin1String("/doxygen/compounddef/detaileddescription"); diff --git a/sources/shiboken2/ApiExtractor/qtdocparser.cpp b/sources/shiboken2/ApiExtractor/qtdocparser.cpp index 809760450..c5ee1743d 100644 --- a/sources/shiboken2/ApiExtractor/qtdocparser.cpp +++ b/sources/shiboken2/ApiExtractor/qtdocparser.cpp @@ -31,8 +31,8 @@ #include "messages.h" #include "reporthandler.h" #include "typesystem.h" +#include "xmlutils.h" -#include #include #include #include @@ -161,7 +161,7 @@ QString QtDocParser::queryFunctionDocumentation(const QString &sourceFileName, const QString &classQuery, const AbstractMetaFunction *func, const DocModificationList &signedModifs, - QXmlQuery &xquery, + const XQueryPtr &xquery, QString *errorMessage) { DocModificationList funcModifs; @@ -231,9 +231,13 @@ void QtDocParser::fillDocumentation(AbstractMetaClass* metaClass) return; } - QXmlQuery xquery; const QString sourceFileName = sourceFile.absoluteFilePath(); - xquery.setFocus(QUrl::fromLocalFile(sourceFileName)); + QString errorMessage; + XQueryPtr xquery = XQuery::create(sourceFileName, &errorMessage); + if (xquery.isNull()) { + qCWarning(lcShiboken, "%s", qPrintable(errorMessage)); + return; + } QString className = metaClass->name(); @@ -258,7 +262,6 @@ void QtDocParser::fillDocumentation(AbstractMetaClass* metaClass) metaClass->setDocumentation(doc); //Functions Documentation - QString errorMessage; const AbstractMetaFunctionList &funcs = DocParser::documentableFunctions(metaClass); for (AbstractMetaFunction *func : funcs) { const QString documentation = @@ -324,8 +327,12 @@ Documentation QtDocParser::retrieveModuleDocumentation(const QString& name) return Documentation(); } - QXmlQuery xquery; - xquery.setFocus(QUrl(sourceFile)); + QString errorMessage; + XQueryPtr xquery = XQuery::create(sourceFile, &errorMessage); + if (xquery.isNull()) { + qCWarning(lcShiboken, "%s", qPrintable(errorMessage)); + return {}; + } // Module documentation QString query = QLatin1String("/WebXML/document/module[@name=\"") diff --git a/sources/shiboken2/ApiExtractor/qtdocparser.h b/sources/shiboken2/ApiExtractor/qtdocparser.h index b5f0e51d8..c4333e820 100644 --- a/sources/shiboken2/ApiExtractor/qtdocparser.h +++ b/sources/shiboken2/ApiExtractor/qtdocparser.h @@ -45,7 +45,7 @@ private: const QString &classQuery, const AbstractMetaFunction *func, const DocModificationList &signedModifs, - QXmlQuery &xquery, + const XQueryPtr &xquery, QString *errorMessage); }; diff --git a/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp b/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp index f615befb4..6acac41d5 100644 --- a/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp @@ -29,6 +29,7 @@ #include "testmodifydocumentation.h" #include +#include #include #include "testutil.h" #include @@ -59,8 +60,16 @@ R"( QCOMPARE(docMods[0].signature(), QString()); QCOMPARE(docMods[1].code().trimmed(), QLatin1String("Some changed contents here")); QCOMPARE(docMods[1].signature(), QString()); + + // Create a temporary directory for the documentation file since libxml2 + // cannot handle Qt resources. + QTemporaryDir tempDir(QDir::tempPath() + QLatin1String("/shiboken_testmodifydocXXXXXX")); + QVERIFY2(tempDir.isValid(), qPrintable(tempDir.errorString())); + const QString docFileName = QLatin1String("a.xml"); + QVERIFY(QFile::copy(QLatin1String(":/") + docFileName, tempDir.filePath(docFileName))); + QtDocParser docParser; - docParser.setDocumentationDataDirectory(QLatin1String(":")); + docParser.setDocumentationDataDirectory(tempDir.path()); docParser.fillDocumentation(classA); const QString actualDocSimplified = classA->documentation().value().simplified(); diff --git a/sources/shiboken2/ApiExtractor/xmlutils.cpp b/sources/shiboken2/ApiExtractor/xmlutils.cpp new file mode 100644 index 000000000..a179412a7 --- /dev/null +++ b/sources/shiboken2/ApiExtractor/xmlutils.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $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 "xmlutils.h" + +#include "xmlutils_qt.h" +#include "xmlutils_libxslt.h" + +XQuery::XQuery() = default; + +XQuery::~XQuery() = default; + +QString XQuery::evaluate(QString xPathExpression, QString *errorMessage) +{ + // XQuery can't have invalid XML characters + xPathExpression.replace(QLatin1Char('&'), QLatin1String("&")); + xPathExpression.replace(QLatin1Char('<'), QLatin1String("<")); + return doEvaluate(xPathExpression, errorMessage); +} + +QSharedPointer XQuery::create(const QString &focus, QString *errorMessage) +{ +#if defined(HAVE_LIBXSLT) + return libXml_createXQuery(focus, errorMessage); +#elif defined(HAVE_QTXMLPATTERNS) + return qt_createXQuery(focus, errorMessage); +#else + *errorMessage = QLatin1String(__FUNCTION__) + QLatin1String(" is not implemented."); + return QSharedPointer(); +#endif +} + +QString xsl_transform(const QString &xml, const QString &xsl, QString *errorMessage) +{ +#if defined(HAVE_LIBXSLT) + return libXslt_transform(xml, xsl, errorMessage); +#elif defined(HAVE_QTXMLPATTERNS) + return qt_xsl_transform(xml, xsl, errorMessage); +#else + *errorMessage = QLatin1String(__FUNCTION__) + QLatin1String(" is not implemented."); + return xml; +#endif +} diff --git a/sources/shiboken2/ApiExtractor/xmlutils.h b/sources/shiboken2/ApiExtractor/xmlutils.h new file mode 100644 index 000000000..879b7757a --- /dev/null +++ b/sources/shiboken2/ApiExtractor/xmlutils.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $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$ +** +****************************************************************************/ +#ifndef XMLUTILS_H +#define XMLUTILS_H + +#include +#include + +class XQuery +{ +public: + Q_DISABLE_COPY(XQuery); + + virtual ~XQuery(); + + QString evaluate(QString xPathExpression, QString *errorMessage); + + static QSharedPointer create(const QString &focus, QString *errorMessage); + +protected: + XQuery(); + + virtual QString doEvaluate(const QString &xPathExpression, QString *errorMessage) = 0; +}; + +QString xsl_transform(const QString &xml, const QString &xsl, QString *errorMessage); + +#endif // XMLUTILS_H diff --git a/sources/shiboken2/ApiExtractor/xmlutils_libxslt.cpp b/sources/shiboken2/ApiExtractor/xmlutils_libxslt.cpp new file mode 100644 index 000000000..e1e185130 --- /dev/null +++ b/sources/shiboken2/ApiExtractor/xmlutils_libxslt.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $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 "xmlutils_libxslt.h" +#include "xmlutils.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +static void cleanup() +{ + xsltCleanupGlobals(); + xmlCleanupParser(); +} + +static void ensureInitialized() +{ + static bool initialized = false; + if (!initialized) { + initialized = true; + xmlInitParser(); + xsltInit(); + qAddPostRoutine(cleanup); + } +} + +namespace { + +// RAI Helpers for cleaning up libxml2/libxslt data + +struct XmlDocDeleter // for std::unique_ptr +{ + void operator()(xmlDocPtr xmlDoc) { xmlFreeDoc(xmlDoc); } +}; + +struct XmlXPathObjectDeleter +{ + void operator()(xmlXPathObjectPtr xPathObject) { xmlXPathFreeObject(xPathObject); } +}; + +struct XmlStyleSheetDeleter // for std::unique_ptr +{ + void operator()(xsltStylesheetPtr xslt) { xsltFreeStylesheet(xslt); } +}; + +struct XmlXPathContextDeleter +{ + void operator()(xmlXPathContextPtr xPathContext) { xmlXPathFreeContext(xPathContext); } +}; + +} // namespace + +using XmlDocUniquePtr = std::unique_ptr; +using XmlPathObjectUniquePtr = std::unique_ptr; +using XmlStyleSheetUniquePtr = std::unique_ptr; +using XmlXPathContextUniquePtr = std::unique_ptr; + +// Helpers for formatting nodes obtained from XPATH queries + +static int qbXmlOutputWriteCallback(void *context, const char *buffer, int len) +{ + static_cast(context)->append(buffer, len); + return len; +} + +static int qbXmlOutputCloseCallback(void * /* context */) { return 0; } + +static QByteArray formatNode(xmlNodePtr node, QString *errorMessage) +{ + QByteArray result; + xmlSaveCtxtPtr saveContext = + xmlSaveToIO(qbXmlOutputWriteCallback, qbXmlOutputCloseCallback, + &result, "UTF-8", 0); + if (!saveContext) { + *errorMessage = QLatin1String("xmlSaveToIO() failed."); + return result; + } + const long saveResult = xmlSaveTree(saveContext, node); + xmlSaveClose(saveContext); + if (saveResult < 0) + *errorMessage = QLatin1String("xmlSaveTree() failed."); + return result; +} + +// XPath expressions +class LibXmlXQuery : public XQuery +{ +public: + explicit LibXmlXQuery(XmlDocUniquePtr &doc, XmlXPathContextUniquePtr &xpathContext) : + m_doc(std::move(doc)), m_xpathContext(std::move(xpathContext)) + { + ensureInitialized(); + } + +protected: + QString doEvaluate(const QString &xPathExpression, QString *errorMessage) override; + +private: + XmlDocUniquePtr m_doc; + XmlXPathContextUniquePtr m_xpathContext; +}; + +QString LibXmlXQuery::doEvaluate(const QString &xPathExpression, QString *errorMessage) +{ + const QByteArray xPathExpressionB = xPathExpression.toUtf8(); + auto xPathExpressionX = reinterpret_cast(xPathExpressionB.constData()); + + XmlPathObjectUniquePtr xPathObject(xmlXPathEvalExpression(xPathExpressionX, m_xpathContext.get())); + if (!xPathObject) { + *errorMessage = QLatin1String("xmlXPathEvalExpression() failed for \"") + xPathExpression + + QLatin1Char('"'); + return QString(); + } + QString result; + if (xmlNodeSetPtr nodeSet = xPathObject->nodesetval) { + for (int n = 0, count = nodeSet->nodeNr; n < count; ++n) { + auto node = nodeSet->nodeTab[n]; + if (node->type == XML_ELEMENT_NODE) { + result += QString::fromLocal8Bit(formatNode(node, errorMessage)); + if (!errorMessage->isEmpty()) + return QString(); + } + } + } + return result; +} + +QSharedPointer libXml_createXQuery(const QString &focus, QString *errorMessage) +{ + XmlDocUniquePtr doc(xmlParseFile(QFile::encodeName(focus).constData())); + if (!doc) { + *errorMessage = QLatin1String("libxml2: Cannot set focus to ") + QDir::toNativeSeparators(focus); + return {}; + } + XmlXPathContextUniquePtr xpathContext(xmlXPathNewContext(doc.get())); + if (!xpathContext) { + *errorMessage = QLatin1String("libxml2: xmlXPathNewContext() failed"); + return {}; + } + return QSharedPointer(new LibXmlXQuery(doc, xpathContext)); +} + +// XSLT transformation + +static const char xsltPrefix[] = R"( + +)"; + +QString libXslt_transform(const QString &xml, QString xsl, QString *errorMessage) +{ + ensureInitialized(); + // Read XML data + if (!xsl.startsWith(QLatin1String("")); + } + const QByteArray xmlData = xml.toUtf8(); + XmlDocUniquePtr xmlDoc(xmlParseMemory(xmlData.constData(), xmlData.size())); + if (!xmlDoc) { + *errorMessage = QLatin1String("xmlParseMemory() failed for XML."); + return xml; + } + + // Read XSL data as a XML file + const QByteArray xslData = xsl.toUtf8(); + // xsltFreeStylesheet will delete this pointer + xmlDocPtr xslDoc = xmlParseMemory(xslData.constData(), xslData.size()); + if (!xslDoc) { + *errorMessage = QLatin1String("xmlParseMemory() failed for XSL \"") + + xsl + QLatin1String("\"."); + return xml; + }; + + // Parse XSL data + XmlStyleSheetUniquePtr xslt(xsltParseStylesheetDoc(xslDoc)); + if (!xslt) { + *errorMessage = QLatin1String("xsltParseStylesheetDoc() failed."); + return xml; + } + + // Apply XSL + XmlDocUniquePtr xslResult(xsltApplyStylesheet(xslt.get(), xmlDoc.get(), nullptr)); + xmlChar *buffer = nullptr; + int bufferSize; + QString result; + if (xsltSaveResultToString(&buffer, &bufferSize, xslResult.get(), xslt.get()) == 0) { + result = QString::fromUtf8(reinterpret_cast(buffer), bufferSize); + std::free(buffer); + } else { + *errorMessage = QLatin1String("xsltSaveResultToString() failed."); + result = xml; + } + return result.trimmed(); +} diff --git a/sources/shiboken2/ApiExtractor/xmlutils_libxslt.h b/sources/shiboken2/ApiExtractor/xmlutils_libxslt.h new file mode 100644 index 000000000..c32b3901d --- /dev/null +++ b/sources/shiboken2/ApiExtractor/xmlutils_libxslt.h @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $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$ +** +****************************************************************************/ +#ifndef XMLUTILS_LIBXSLT_H +#define XMLUTILS_LIBXSLT_H + +#include +#include + +class XQuery; + +QSharedPointer libXml_createXQuery(const QString &focus, QString *errorMessage); + +QString libXslt_transform(const QString &xml, QString xsl, QString *errorMessage); + +#endif // XMLUTILS_LIBXSLT_H diff --git a/sources/shiboken2/ApiExtractor/xmlutils_qt.cpp b/sources/shiboken2/ApiExtractor/xmlutils_qt.cpp new file mode 100644 index 000000000..f703bc5f9 --- /dev/null +++ b/sources/shiboken2/ApiExtractor/xmlutils_qt.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $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 "xmlutils.h" +#include "xmlutils_qt.h" + +#include + +#include +#include + +class QtXQuery : public XQuery +{ +public: + QtXQuery() = default; + + bool setFocus(const QString &fileName) + { return m_xquery.setFocus(QUrl::fromLocalFile(fileName)); } + +protected: + QString doEvaluate(const QString &xPathExpression, QString *errorMessage) override; + +private: + QXmlQuery m_xquery; +}; + +QString QtXQuery::doEvaluate(const QString &xPathExpression, QString *errorMessage) +{ + m_xquery.setQuery(xPathExpression); + if (!m_xquery.isValid()) { + *errorMessage = QLatin1String("QXmlQuery: Bad query: \"") + xPathExpression + + QLatin1Char('"'); + return QString(); + } + + QString result; + m_xquery.evaluateTo(&result); + return result; +} + +QSharedPointer qt_createXQuery(const QString &focus, QString *errorMessage) +{ + QSharedPointer result(new QtXQuery); + if (!result->setFocus(focus)) { + *errorMessage = QLatin1String("QXmlQuery: Cannot set focus to ") + QDir::toNativeSeparators(focus); + result.reset(); + } + return std::move(result); +} + +// XSLT transformation + +static const char xsltPrefix[] = R"( + +)"; + +QString qt_xsl_transform(const QString &xml, QString xsl, QString *errorMessage) +{ + QXmlQuery query(QXmlQuery::XSLT20); + if (!xsl.startsWith(QLatin1String("")); + } + query.setFocus(xml); + query.setQuery(xsl); + if (!query.isValid()) { + *errorMessage = QLatin1String("QXmlQuery: Invalid query \"") + xsl + + QLatin1String("\"."); + return xml; + } + QString result; + if (!query.evaluateTo(&result)) { + *errorMessage = QLatin1String("QXmlQuery: evaluate() failed."); + return xml; + } + return result.trimmed(); +} diff --git a/sources/shiboken2/ApiExtractor/xmlutils_qt.h b/sources/shiboken2/ApiExtractor/xmlutils_qt.h new file mode 100644 index 000000000..a9c9adfa2 --- /dev/null +++ b/sources/shiboken2/ApiExtractor/xmlutils_qt.h @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $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$ +** +****************************************************************************/ +#ifndef XMLUTILS_QT_H +#define XMLUTILS_QT_H + +#include +#include + +class XQuery; + +QSharedPointer qt_createXQuery(const QString &focus, QString *errorMessage); + +QString qt_xsl_transform(const QString &xml, QString xsl, QString *errorMessage); + +#endif // XMLUTILS_QT_H diff --git a/sources/shiboken2/CMakeLists.txt b/sources/shiboken2/CMakeLists.txt index 12429c657..950aa6215 100644 --- a/sources/shiboken2/CMakeLists.txt +++ b/sources/shiboken2/CMakeLists.txt @@ -24,7 +24,12 @@ if(BUILD_TESTS) find_package(Qt5Test 5.12 REQUIRED) endif() -if(NOT Qt5XmlPatterns_FOUND OR NOT Qt5Xml_FOUND) +set(HAS_LIBXSLT 0) +if (LIBXSLT_FOUND AND LIBXML2_FOUND) + set(HAS_LIBXSLT 1) +endif() + +if(NOT Qt5XmlPatterns_FOUND AND NOT HAS_LIBXSLT) set(DISABLE_DOCSTRINGS TRUE) message(WARNING "Documentation will not be built due to missing dependency (no Qt5XmlPatterns found).") diff --git a/sources/shiboken2/generator/CMakeLists.txt b/sources/shiboken2/generator/CMakeLists.txt index 745c366f5..15b965d9d 100644 --- a/sources/shiboken2/generator/CMakeLists.txt +++ b/sources/shiboken2/generator/CMakeLists.txt @@ -24,7 +24,6 @@ target_link_libraries(shiboken2 apiextractor Qt5::Core) if (NOT DISABLE_DOCSTRINGS) target_sources(shiboken2 PRIVATE qtdoc/qtdocgenerator.cpp) target_compile_definitions(shiboken2 PUBLIC DOCSTRINGS_ENABLED) - target_link_libraries(shiboken2 Qt5::XmlPatterns) endif() configure_file(shibokenconfig.h.in "${CMAKE_CURRENT_BINARY_DIR}/shibokenconfig.h" @ONLY) -- cgit v1.2.3