diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2018-03-07 11:06:08 +0100 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2018-03-07 11:07:20 +0000 |
commit | 507beab92fcb39e9b47d465a51d649f7ea6935d5 (patch) | |
tree | 9cab98c62d18c3db6afe0c63200426124e8ad4eb /sources/shiboken2/ApiExtractor | |
parent | f92869113a1421494e62a17c1ab3bd86cdff7e18 (diff) |
shiboken/qtdocgenerator: Fall back to QtXmlPatterns when libxml/libxslt cannot be found
In PySide2, there is one XSL-based documentation modification which QXmlQuery
can handle.
However, due to QTBUG-66925, it is not a full replacement, since the
issue (XPATH specifying the number of the element to match) might be
useful for documentation modification.
Add QtXmlPatterns as fallback and modify the test accordingly.
Task-number: PYSIDE-363
Change-Id: I969fbe210725bb748d76028c6f542bae6b471a76
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'sources/shiboken2/ApiExtractor')
-rw-r--r-- | sources/shiboken2/ApiExtractor/CMakeLists.txt | 18 | ||||
-rw-r--r-- | sources/shiboken2/ApiExtractor/docparser.cpp | 114 | ||||
-rw-r--r-- | sources/shiboken2/ApiExtractor/docparser.h | 2 | ||||
-rw-r--r-- | sources/shiboken2/ApiExtractor/tests/a.xml | 3 | ||||
-rw-r--r-- | sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp | 56 |
5 files changed, 154 insertions, 39 deletions
diff --git a/sources/shiboken2/ApiExtractor/CMakeLists.txt b/sources/shiboken2/ApiExtractor/CMakeLists.txt index f2af51c02..4355e32ef 100644 --- a/sources/shiboken2/ApiExtractor/CMakeLists.txt +++ b/sources/shiboken2/ApiExtractor/CMakeLists.txt @@ -5,11 +5,13 @@ find_package(LibXslt 1.1.19) option(DISABLE_DOCSTRINGS "Disable documentation extraction." FALSE) +set (USE_LIBXSLT 0) if (NOT DISABLE_DOCSTRINGS) - if (NOT LIBXSLT_FOUND OR NOT LIBXML2_FOUND) - set(DISABLE_DOCSTRINGS TRUE CACHE BOOL "Disable doc strings" PARENT_SCOPE) - set(DISABLE_DOCSTRINGS TRUE) - message(WARNING "libxslt and/or libxml not found, disabling support for doc strings!") + if (LIBXSLT_FOUND AND LIBXML2_FOUND) + add_definitions(-DHAVE_LIBXSLT) + set (USE_LIBXSLT 1) + else() + message(WARNING "libxslt and/or libxml not found, falling back to QtXmlPatterns (QTBUG-66925)") endif() endif() @@ -65,8 +67,12 @@ if (NOT DISABLE_DOCSTRINGS) doxygenparser.cpp qtdocparser.cpp ) - set(APIEXTRACTOR_EXTRA_INCLUDES ${APIEXTRACTOR_EXTRA_INCLUDES} ${LIBXSLT_INCLUDE_DIR} ${LIBXML2_INCLUDE_DIR}) - set(APIEXTRACTOR_EXTRA_LIBRARIES ${APIEXTRACTOR_EXTRA_LIBRARIES} ${LIBXSLT_LIBRARIES} ${LIBXML2_LIBRARIES}) + set(APIEXTRACTOR_EXTRA_INCLUDES ${APIEXTRACTOR_EXTRA_INCLUDES}) + set(APIEXTRACTOR_EXTRA_LIBRARIES ${APIEXTRACTOR_EXTRA_LIBRARIES}) + if (USE_LIBXSLT) + list(APPEND APIEXTRACTOR_EXTRA_INCLUDES ${LIBXSLT_INCLUDE_DIR} ${LIBXML2_INCLUDE_DIR}) + list(APPEND APIEXTRACTOR_EXTRA_LIBRARIES ${LIBXSLT_LIBRARIES} ${LIBXML2_LIBRARIES}) + endif() endif() 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) diff --git a/sources/shiboken2/ApiExtractor/docparser.cpp b/sources/shiboken2/ApiExtractor/docparser.cpp index 1d1783f05..d24e09acd 100644 --- a/sources/shiboken2/ApiExtractor/docparser.cpp +++ b/sources/shiboken2/ApiExtractor/docparser.cpp @@ -27,6 +27,7 @@ ****************************************************************************/ #include "docparser.h" #include "abstractmetalang.h" +#include "reporthandler.h" #include "typesystem.h" #include <QtCore/QDebug> #include <QtCore/QDir> @@ -35,12 +36,18 @@ #include <QBuffer> #include <cstdlib> -#include <libxslt/xsltutils.h> -#include <libxslt/transform.h> +#ifdef HAVE_LIBXSLT +# include <libxslt/xsltutils.h> +# include <libxslt/transform.h> +#endif + +#include <algorithm> DocParser::DocParser() { +#ifdef HAVE_LIBXSLT xmlSubstituteEntitiesDefault(1); +#endif } DocParser::~DocParser() @@ -142,6 +149,7 @@ QString DocParser::msgCannotFindDocumentation(const QString &fileName, query); } +#ifdef HAVE_LIBXSLT namespace { @@ -170,23 +178,57 @@ struct XslResources }; } // namespace +#endif // HAVE_LIBXSLT -QString DocParser::applyDocModifications(const DocModificationList& mods, const QString& xml) const +static inline bool isXpathDocModification(const DocModification &mod) { - if (mods.isEmpty() || xml.isEmpty()) - return xml; + return mod.mode() == TypeSystem::DocModificationXPathReplace; +} - bool hasXPathBasedModification = false; +QString msgXpathDocModificationError(const DocModificationList& mods, + const QString &what) +{ + QString result; + QTextStream str(&result); + str << "Error when applying modifications ("; for (const DocModification &mod : mods) { - if (mod.mode() == TypeSystem::DocModificationXPathReplace) { - hasXPathBasedModification = true; - break; + if (isXpathDocModification(mod)) { + str << '"' << mod.xpath() << "\" -> \""; + const QString simplified = mod.code().simplified(); + if (simplified.size() > 20) + str << simplified.leftRef(20) << "..."; + else + str << simplified; + str << '"'; } } + str << "): " << what; + return result; +} - if (!hasXPathBasedModification) +QString DocParser::applyDocModifications(const DocModificationList& mods, const QString& xml) const +{ + 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" @@ -200,7 +242,7 @@ QString DocParser::applyDocModifications(const DocModificationList& mods, const "</xsl:template>\n" ); for (const DocModification &mod : mods) { - if (mod.mode() == TypeSystem::DocModificationXPathReplace) { + if (isXpathDocModification(mod)) { QString xpath = mod.xpath(); xpath.replace(QLatin1Char('"'), QLatin1String(""")); xsl += QLatin1String("<xsl:template match=\"") @@ -240,8 +282,54 @@ QString DocParser::applyDocModifications(const DocModificationList& mods, const } else { result = xml; } - - Q_ASSERT(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) { + if (isXpathDocModification(mod)) { + QString xpath = mod.xpath(); + xpath.replace(QLatin1Char('"'), QLatin1String(""")); + xsl += QLatin1String("<xsl:template match=\"") + + xpath + QLatin1String("\">") + + mod.code() + QLatin1String("</xsl:template>\n"); + } + } + xsl += QLatin1String("</xsl:stylesheet>"); + + QXmlQuery query(QXmlQuery::XSLT20); + query.setFocus(xml); + query.setQuery(xsl); + if (!query.isValid()) { + qCWarning(lcShiboken, "%s", + qPrintable(msgXpathDocModificationError(mods, QLatin1String("Invalid query.")))); + return xml; + } + QString result; + if (!query.evaluateTo(&result)) { + qCWarning(lcShiboken, "%s", + qPrintable(msgXpathDocModificationError(mods, QLatin1String("evaluate() failed.")))); + return xml; + } + return result.trimmed(); +} diff --git a/sources/shiboken2/ApiExtractor/docparser.h b/sources/shiboken2/ApiExtractor/docparser.h index 1770815b0..1da58fc11 100644 --- a/sources/shiboken2/ApiExtractor/docparser.h +++ b/sources/shiboken2/ApiExtractor/docparser.h @@ -143,6 +143,8 @@ private: QString execXQuery(QXmlQuery& 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/tests/a.xml b/sources/shiboken2/ApiExtractor/tests/a.xml index 1c6d62a17..3c09d3800 100644 --- a/sources/shiboken2/ApiExtractor/tests/a.xml +++ b/sources/shiboken2/ApiExtractor/tests/a.xml @@ -1,9 +1,10 @@ <?xml version="1.0" ?> - +<!-- Sample for testModifyDocumentation --> <WebXML> <document> <class name="A"> <description>oi + <brief>Brief description</brief> <para>Paragraph number 1</para> <para>Paragraph number 2</para> <para>Paragraph number 3</para> diff --git a/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp b/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp index d56186479..cc95186ef 100644 --- a/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp +++ b/sources/shiboken2/ApiExtractor/tests/testmodifydocumentation.cpp @@ -38,35 +38,53 @@ void TestModifyDocumentation::testModifyDocumentation() { const char* cppCode ="struct B { void b(); }; class A {};\n"; - const char* xmlCode = "<typesystem package=\"Foo\">\n\ - <value-type name='B'>\n\ - <modify-function signature='b()' remove='all'/>\n\ - </value-type>\n\ - <value-type name='A'>\n\ - <modify-documentation xpath='description/para[3]'>\n\ - <para>Some changed contents here</para>\n\ - </modify-documentation>\n\ - </value-type>\n\ - </typesystem>\n"; + const char xmlCode[] = +R"(<typesystem package="Foo"> + <value-type name='B'> + <modify-function signature='b()' remove='all'/> + </value-type> + <value-type name='A'> + <modify-documentation xpath='description/brief'><brief>Modified Brief</brief></modify-documentation> + <modify-documentation xpath='description/para[3]'><para>Some changed contents here</para></modify-documentation> + </value-type> +</typesystem> +)"; QScopedPointer<AbstractMetaBuilder> builder(TestUtil::parse(cppCode, xmlCode)); QVERIFY(!builder.isNull()); AbstractMetaClass *classA = AbstractMetaClass::findClass(builder->classes(), QLatin1String("A")); QVERIFY(classA); DocModificationList docMods = classA->typeEntry()->docModifications(); - QCOMPARE(docMods.count(), 1); - QCOMPARE(docMods[0].code().trimmed(), QLatin1String("<para>Some changed contents here</para>")); + QCOMPARE(docMods.count(), 2); + QCOMPARE(docMods[0].code().trimmed(), QLatin1String("<brief>Modified Brief</brief>")); QCOMPARE(docMods[0].signature(), QString()); + QCOMPARE(docMods[1].code().trimmed(), QLatin1String("<para>Some changed contents here</para>")); + QCOMPARE(docMods[1].signature(), QString()); QtDocParser docParser; docParser.setDocumentationDataDirectory(QDir::currentPath()); docParser.fillDocumentation(classA); - QVERIFY(!classA->documentation().value().trimmed().isEmpty()); - QCOMPARE(classA->documentation().value(), QLatin1String("<?xml version=\"1.0\"?>\n\ -<description>oi\n\ - <para>Paragraph number 1</para>\n\ - <para>Paragraph number 2</para>\n\ - <para>Some changed contents here</para>\n\ -</description>")); + const QString actualDocSimplified = classA->documentation().value().simplified(); + QVERIFY(!actualDocSimplified.isEmpty()); + +const char expectedDoc[] = +R"(<?xml version="1.0"?> +<description>oi +<brief>Modified Brief</brief> +<para>Paragraph number 1</para> +<para>Paragraph number 2</para> +<para>Some changed contents here</para> +</description> +)"; + const QString expectedDocSimplified = QString::fromLatin1(expectedDoc).simplified(); + // Check whether the first modification worked. + QVERIFY(actualDocSimplified.contains(QLatin1String("Modified Brief"))); + +#ifndef HAVE_LIBXSLT + // QtXmlPatterns is unable to handle para[3] in style sheets, + // this only works in its XPath search. + QEXPECT_FAIL("", "QtXmlPatterns cannot handle para[3] (QTBUG-66925)", Abort); +#endif + QCOMPARE(actualDocSimplified, expectedDocSimplified); } // We expand QTEST_MAIN macro but using QCoreApplication instead of QApplication |