aboutsummaryrefslogtreecommitdiffstats
path: root/sources/shiboken2
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2019-04-26 08:00:43 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2019-04-26 08:00:48 +0200
commit00394c4773c89922e8ceef50155bc56323cf16b0 (patch)
treec26273428612712c59c257fd9545fd16428735c0 /sources/shiboken2
parentdc4ad7f211db52d614426c4f2b7b808c6200ae85 (diff)
parent1beda098a4ce3128b1669b66230dbfa3c3c1a821 (diff)
Merge remote-tracking branch 'origin/5.13' into dev
Diffstat (limited to 'sources/shiboken2')
-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/reporthandler.cpp17
-rw-r--r--sources/shiboken2/ApiExtractor/reporthandler.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
-rw-r--r--sources/shiboken2/generator/main.cpp11
18 files changed, 652 insertions, 199 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/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 <QtCore/QElapsedTimer>
#include <QtCore/QSet>
#include <cstring>
#include <cstdarg>
@@ -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/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)
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 <QCoreApplication>
-#include <QElapsedTimer>
#include <QLibrary>
#include <QtCore/QFile>
#include <QtCore/QDir>
@@ -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;