From 507beab92fcb39e9b47d465a51d649f7ea6935d5 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 7 Mar 2018 11:06:08 +0100 Subject: 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 --- sources/shiboken2/ApiExtractor/CMakeLists.txt | 18 ++-- sources/shiboken2/ApiExtractor/docparser.cpp | 114 ++++++++++++++++++--- sources/shiboken2/ApiExtractor/docparser.h | 2 + sources/shiboken2/ApiExtractor/tests/a.xml | 3 +- .../ApiExtractor/tests/testmodifydocumentation.cpp | 56 ++++++---- 5 files changed, 154 insertions(+), 39 deletions(-) (limited to 'sources/shiboken2/ApiExtractor') 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 #include @@ -35,12 +36,18 @@ #include #include -#include -#include +#ifdef HAVE_LIBXSLT +# include +# include +#endif + +#include 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("\n" "\n" "\n" @@ -200,7 +242,7 @@ QString DocParser::applyDocModifications(const DocModificationList& mods, const "\n" ); for (const DocModification &mod : mods) { - if (mod.mode() == TypeSystem::DocModificationXPathReplace) { + if (isXpathDocModification(mod)) { QString xpath = mod.xpath(); xpath.replace(QLatin1Char('"'), QLatin1String(""")); xsl += QLatin1String(" + + + \n" + + + + + + + +)"; + + QString xsl = QLatin1String(xslPrefix); + for (const DocModification &mod : mods) { + if (isXpathDocModification(mod)) { + QString xpath = mod.xpath(); + xpath.replace(QLatin1Char('"'), QLatin1String(""")); + xsl += QLatin1String("") + + mod.code() + QLatin1String("\n"); + } + } + xsl += QLatin1String(""); + + 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 @@ - + oi + Brief description Paragraph number 1 Paragraph number 2 Paragraph number 3 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 = "\n\ - \n\ - \n\ - \n\ - \n\ - \n\ - <para>Some changed contents here</para>\n\ - \n\ - \n\ - \n"; + const char xmlCode[] = +R"( + + + + + <brief>Modified Brief</brief> + <para>Some changed contents here</para> + + +)"; QScopedPointer 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("Some changed contents here")); + QCOMPARE(docMods.count(), 2); + QCOMPARE(docMods[0].code().trimmed(), QLatin1String("Modified Brief")); QCOMPARE(docMods[0].signature(), QString()); + QCOMPARE(docMods[1].code().trimmed(), QLatin1String("Some changed contents here")); + 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("\n\ -oi\n\ - Paragraph number 1\n\ - Paragraph number 2\n\ - Some changed contents here\n\ -")); + const QString actualDocSimplified = classA->documentation().value().simplified(); + QVERIFY(!actualDocSimplified.isEmpty()); + +const char expectedDoc[] = +R"( +oi +Modified Brief +Paragraph number 1 +Paragraph number 2 +Some changed contents here + +)"; + 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 -- cgit v1.2.3