aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2019-04-18 12:41:14 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2019-04-25 11:53:15 +0000
commit21077bec48f2ca16ec0fed25e0c0af62317606d9 (patch)
tree8f3e8e6c636ce96c7385745a8b15959c01de426e
parente336872828de86146059de0f62ed06afaaf64d8d (diff)
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 <cristian.maureira-fredes@qt.io>
-rw-r--r--sources/shiboken2/ApiExtractor/CMakeLists.txt26
-rw-r--r--sources/shiboken2/ApiExtractor/docparser.cpp183
-rw-r--r--sources/shiboken2/ApiExtractor/docparser.h17
-rw-r--r--sources/shiboken2/ApiExtractor/doxygenparser.cpp19
-rw-r--r--sources/shiboken2/ApiExtractor/qtdocparser.cpp21
-rw-r--r--sources/shiboken2/ApiExtractor/qtdocparser.h2
-rw-r--r--sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp11
-rw-r--r--sources/shiboken2/ApiExtractor/xmlutils.cpp68
-rw-r--r--sources/shiboken2/ApiExtractor/xmlutils.h53
-rw-r--r--sources/shiboken2/ApiExtractor/xmlutils_libxslt.cpp231
-rw-r--r--sources/shiboken2/ApiExtractor/xmlutils_libxslt.h40
-rw-r--r--sources/shiboken2/ApiExtractor/xmlutils_qt.cpp102
-rw-r--r--sources/shiboken2/ApiExtractor/xmlutils_qt.h40
-rw-r--r--sources/shiboken2/CMakeLists.txt7
-rw-r--r--sources/shiboken2/generator/CMakeLists.txt1
15 files changed, 631 insertions, 190 deletions
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 <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QTextStream>
-#include <QtXmlPatterns/QXmlQuery>
#include <QBuffer>
#include <cstdlib>
@@ -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("&amp;"));
- escapedQuery.replace(QLatin1Char('<'), QLatin1String("&lt;"));
- 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"(<xsl:template match="/">
+ <xsl:apply-templates />
+</xsl:template>
+<xsl:template match="*">
+<xsl:copy>
+ <xsl:copy-of select="@*"/>
+ <xsl:apply-templates/>
+</xsl:copy>
+</xsl:template>
+)";
+
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("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
- "<xsl:transform version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n"
- "<xsl:template match=\"/\">\n"
- " <xsl:apply-templates />\n"
- "</xsl:template>\n"
- "<xsl:template match=\"*\">\n"
- "<xsl:copy>\n"
- " <xsl:copy-of select=\"@*\"/>\n"
- " <xsl:apply-templates/>\n"
- "</xsl:copy>\n"
- "</xsl:template>\n"
- );
- for (const DocModification &mod : mods) {
- if (isXpathDocModification(mod)) {
- QString xpath = mod.xpath();
- xpath.replace(QLatin1Char('"'), QLatin1String("&quot;"));
- xsl += QLatin1String("<xsl:template match=\"")
- + xpath + QLatin1String("\">")
- + mod.code() + QLatin1String("</xsl:template>\n");
- }
- }
- xsl += QLatin1String("</xsl:transform>");
-
- 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<char*>(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"(<?xml version="1.0" encoding="UTF-8"?>
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
- <xsl:template match="/">
- <xsl:apply-templates/>\n"
- </xsl:template>
- <xsl:template match="*">
- <xsl:copy>
- <xsl:copy-of select="@*"/>
- <xsl:apply-templates/>
- </xsl:copy>
- </xsl:template>
-)";
QString xsl = QLatin1String(xslPrefix);
for (const DocModification &mod : mods) {
@@ -257,21 +134,17 @@ R"(<?xml version="1.0" encoding="UTF-8"?>
+ mod.code() + QLatin1String("</xsl:template>\n");
}
}
- xsl += QLatin1String("</xsl:stylesheet>");
- 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 <QtCore/QString>
-
-QT_BEGIN_NAMESPACE
-class QDomDocument;
-class QDomNode;
-class QXmlQuery;
-QT_END_NAMESPACE
+#include <QtCore/QSharedPointer>
class AbstractMetaClass;
class DocModification;
class Documentation;
+class XQuery;
+
class DocParser
{
public:
+ using XQueryPtr = QSharedPointer<XQuery>;
+
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 <QtXmlPatterns/QXmlQuery>
#include <QtCore/QFile>
#include <QtCore/QDir>
@@ -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 <QtXmlPatterns/QXmlQuery>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QTextStream>
@@ -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 <QCoreApplication>
+#include <QtCore/QTemporaryDir>
#include <QtTest/QTest>
#include "testutil.h"
#include <abstractmetalang.h>
@@ -59,8 +60,16 @@ R"(<typesystem package="Foo">
QCOMPARE(docMods[0].signature(), QString());
QCOMPARE(docMods[1].code().trimmed(), QLatin1String("<para>Some changed contents here</para>"));
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("&amp;"));
+ xPathExpression.replace(QLatin1Char('<'), QLatin1String("&lt;"));
+ return doEvaluate(xPathExpression, errorMessage);
+}
+
+QSharedPointer<XQuery> 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<XQuery>();
+#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 <QtCore/QSharedPointer>
+#include <QtCore/QString>
+
+class XQuery
+{
+public:
+ Q_DISABLE_COPY(XQuery);
+
+ virtual ~XQuery();
+
+ QString evaluate(QString xPathExpression, QString *errorMessage);
+
+ static QSharedPointer<XQuery> 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 <QtCore/QByteArray>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDir>
+#include <QtCore/QFile>
+#include <QtCore/QString>
+
+#include <libxslt/xsltutils.h>
+#include <libxslt/transform.h>
+
+#include <libxml/xmlsave.h>
+#include <libxml/xpath.h>
+
+#include <cstdlib>
+#include <memory>
+
+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<xmlDoc>
+{
+ void operator()(xmlDocPtr xmlDoc) { xmlFreeDoc(xmlDoc); }
+};
+
+struct XmlXPathObjectDeleter
+{
+ void operator()(xmlXPathObjectPtr xPathObject) { xmlXPathFreeObject(xPathObject); }
+};
+
+struct XmlStyleSheetDeleter // for std::unique_ptr<xsltStylesheet>
+{
+ void operator()(xsltStylesheetPtr xslt) { xsltFreeStylesheet(xslt); }
+};
+
+struct XmlXPathContextDeleter
+{
+ void operator()(xmlXPathContextPtr xPathContext) { xmlXPathFreeContext(xPathContext); }
+};
+
+} // namespace
+
+using XmlDocUniquePtr = std::unique_ptr<xmlDoc, XmlDocDeleter>;
+using XmlPathObjectUniquePtr = std::unique_ptr<xmlXPathObject, XmlXPathObjectDeleter>;
+using XmlStyleSheetUniquePtr = std::unique_ptr<xsltStylesheet, XmlStyleSheetDeleter>;
+using XmlXPathContextUniquePtr = std::unique_ptr<xmlXPathContext, XmlXPathContextDeleter>;
+
+// Helpers for formatting nodes obtained from XPATH queries
+
+static int qbXmlOutputWriteCallback(void *context, const char *buffer, int len)
+{
+ static_cast<QByteArray *>(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<const xmlChar *>(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<XQuery> 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<XQuery>(new LibXmlXQuery(doc, xpathContext));
+}
+
+// XSLT transformation
+
+static const char xsltPrefix[] = R"(<?xml version="1.0" encoding="UTF-8" ?>
+ <xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+)";
+
+QString libXslt_transform(const QString &xml, QString xsl, QString *errorMessage)
+{
+ ensureInitialized();
+ // Read XML data
+ if (!xsl.startsWith(QLatin1String("<?xml"))) {
+ xsl.prepend(QLatin1String(xsltPrefix));
+ xsl.append(QLatin1String("</xsl:transform>"));
+ }
+ 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<char*>(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 <QtCore/QString>
+#include <QtCore/QSharedPointer>
+
+class XQuery;
+
+QSharedPointer<XQuery> 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 <QtXmlPatterns/QXmlQuery>
+
+#include <QtCore/QDir>
+#include <QtCore/QUrl>
+
+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<XQuery> qt_createXQuery(const QString &focus, QString *errorMessage)
+{
+ QSharedPointer<QtXQuery> 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"(<?xml version="1.0" encoding="UTF-8"?>
+ <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+)";
+
+QString qt_xsl_transform(const QString &xml, QString xsl, QString *errorMessage)
+{
+ QXmlQuery query(QXmlQuery::XSLT20);
+ if (!xsl.startsWith(QLatin1String("<?xml"))) {
+ xsl.prepend(QLatin1String(xsltPrefix));
+ xsl.append(QLatin1String("</xsl:stylesheet>"));
+ }
+ 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 <QtCore/QString>
+#include <QtCore/QSharedPointer>
+
+class XQuery;
+
+QSharedPointer<XQuery> 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)