summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Smith <[email protected]>2014-10-09 12:44:07 +0200
committerMartin Smith <[email protected]>2014-10-17 10:43:05 +0200
commit4f1de1432c0a028ac68d5a800f3b670ce539d848 (patch)
tree0a99b2361d23b986c62b0b4451eec16b73cc93a2
parent77165553af2e541ca01ec6bf47461ba910d9519b (diff)
qdoc: Generate QA pages
qdoc has a new command line option, -write-qa-pages. Using this flag on the command line will tell qdoc to generate a QA html page in each module's output directory. The QA page contains information that is useful for Quality Assurance checking of the module's docs. The QA file name begins with "aaa" so it will always be listed at the top of the output directory. The file name for the QA file for QtCore, for example, is aaa-qtcore-qa-page.html. Currently, the QA page only contains a report listing the intermodule link count for each module that is the target of links from the documented module. The link report can be used to optimize the search order qdoc uses when resolving inter-module links. By default, the search order is the same as the ordering of the modules in the depends list in the .qdocconf file. Using the report, the user can reorder that list according to the number of links found in each module. i.e. in descending order of link count. The modules are listed in descending order of link count. Additionally, an actual depends variable is printed. It can be cut and pasted into the module's qdocconf file. Change-Id: I442596aeb54dcdd5db4a0821096a5273c15627e6 Task-number: QTBUG-41850 Reviewed-by: Topi Reiniƶ <[email protected]>
-rw-r--r--src/tools/qdoc/config.cpp1
-rw-r--r--src/tools/qdoc/config.h2
-rw-r--r--src/tools/qdoc/generator.cpp7
-rw-r--r--src/tools/qdoc/generator.h8
-rw-r--r--src/tools/qdoc/htmlgenerator.cpp48
-rw-r--r--src/tools/qdoc/htmlgenerator.h1
-rw-r--r--src/tools/qdoc/location.cpp12
-rw-r--r--src/tools/qdoc/location.h4
-rw-r--r--src/tools/qdoc/main.cpp18
-rw-r--r--src/tools/qdoc/qdocdatabase.cpp57
-rw-r--r--src/tools/qdoc/qdocdatabase.h13
-rw-r--r--src/tools/qdoc/tree.cpp1
-rw-r--r--src/tools/qdoc/tree.h4
13 files changed, 163 insertions, 13 deletions
diff --git a/src/tools/qdoc/config.cpp b/src/tools/qdoc/config.cpp
index bd72fc106be..f73ac147b80 100644
--- a/src/tools/qdoc/config.cpp
+++ b/src/tools/qdoc/config.cpp
@@ -119,6 +119,7 @@ QString ConfigStrings::FILEEXTENSIONS = QStringLiteral("fileextensions");
QString ConfigStrings::IMAGEEXTENSIONS = QStringLiteral("imageextensions");
QString ConfigStrings::QMLONLY = QStringLiteral("qmlonly");
QString ConfigStrings::QMLTYPESPAGE = QStringLiteral("qmltypespage");
+QString ConfigStrings::WRITEQAPAGES = QStringLiteral("writeqapages");
/*!
An entry in a stack, where each entry is a list
diff --git a/src/tools/qdoc/config.h b/src/tools/qdoc/config.h
index c4d4f27a9ee..a8364487195 100644
--- a/src/tools/qdoc/config.h
+++ b/src/tools/qdoc/config.h
@@ -231,6 +231,7 @@ struct ConfigStrings
static QString IMAGEEXTENSIONS;
static QString QMLONLY;
static QString QMLTYPESPAGE;
+ static QString WRITEQAPAGES;
};
#define CONFIG_ALIAS ConfigStrings::ALIAS
@@ -305,6 +306,7 @@ struct ConfigStrings
#define CONFIG_IMAGEEXTENSIONS ConfigStrings::IMAGEEXTENSIONS
#define CONFIG_QMLONLY ConfigStrings::QMLONLY
#define CONFIG_QMLTYPESPAGE ConfigStrings::QMLTYPESPAGE
+#define CONFIG_WRITEQAPAGES ConfigStrings::WRITEQAPAGES
QT_END_NAMESPACE
diff --git a/src/tools/qdoc/generator.cpp b/src/tools/qdoc/generator.cpp
index 6f53783aaff..db531e39364 100644
--- a/src/tools/qdoc/generator.cpp
+++ b/src/tools/qdoc/generator.cpp
@@ -64,7 +64,7 @@ QString Generator::outSubdir_;
QStringList Generator::outFileNames_;
QSet<QString> Generator::outputFormats;
QHash<QString, QString> Generator::outputPrefixes;
-QString Generator::project;
+QString Generator::project_;
QStringList Generator::scriptDirs;
QStringList Generator::scriptFiles;
QString Generator::sinceTitles[] =
@@ -94,6 +94,7 @@ bool Generator::autolinkErrors_ = false;
bool Generator::redirectDocumentationToDevNull_ = false;
Generator::QDocPass Generator::qdocPass_ = Generator::Neither;
bool Generator::qdocSingleExec_ = false;
+bool Generator::qdocWriteQaPages_ = false;
bool Generator::useOutputSubdirs_ = true;
void Generator::startDebugging(const QString& message)
@@ -317,7 +318,7 @@ QString Generator::fileBase(const Node *node) const
if (node->isExample() || node->isExampleFile()) {
QString modPrefix(node->moduleName());
if (modPrefix.isEmpty()) {
- modPrefix = project;
+ modPrefix = project_;
}
base.prepend(modPrefix.toLower() + QLatin1Char('-'));
}
@@ -1646,7 +1647,7 @@ void Generator::initialize(const Config &config)
++n;
}
- project = config.getString(CONFIG_PROJECT);
+ project_ = config.getString(CONFIG_PROJECT);
QStringList prefixes = config.getStringList(CONFIG_OUTPUTPREFIXES);
if (!prefixes.isEmpty()) {
diff --git a/src/tools/qdoc/generator.h b/src/tools/qdoc/generator.h
index 306a1596ae4..b1faf02ae97 100644
--- a/src/tools/qdoc/generator.h
+++ b/src/tools/qdoc/generator.h
@@ -95,8 +95,10 @@ public:
static bool preparing() { return (qdocPass_ == Prepare); }
static bool generating() { return (qdocPass_ == Generate); }
static bool singleExec() { return qdocSingleExec_; }
+ static bool writeQaPages() { return qdocWriteQaPages_; }
static void setSingleExec() { qdocSingleExec_ = true; }
- static QString defaultModuleName() { return project; }
+ static void setWriteQaPages() { qdocWriteQaPages_ = true; }
+ static QString defaultModuleName() { return project_; }
static void resetUseOutputSubdirs() { useOutputSubdirs_ = false; }
static bool useOutputSubdirs() { return useOutputSubdirs_; }
@@ -105,6 +107,7 @@ protected:
virtual void endSubPage();
virtual QString fileBase(const Node* node) const;
virtual QString fileExtension() const = 0;
+ virtual void generateQAPage() { }
virtual void generateAlsoList(const Node *node, CodeMarker *marker);
virtual int generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker);
virtual void generateBody(const Node *node, CodeMarker *marker);
@@ -200,7 +203,7 @@ private:
static QStringList imageDirs;
static QStringList imageFiles;
static QMap<QString, QStringList> imgFileExts;
- static QString project;
+ static QString project_;
static QString outDir_;
static QString outSubdir_;
static QStringList outFileNames_;
@@ -216,6 +219,7 @@ private:
static bool redirectDocumentationToDevNull_;
static QDocPass qdocPass_;
static bool qdocSingleExec_;
+ static bool qdocWriteQaPages_;
static bool useOutputSubdirs_;
void generateReimplementedFrom(const FunctionNode *func, CodeMarker *marker);
diff --git a/src/tools/qdoc/htmlgenerator.cpp b/src/tools/qdoc/htmlgenerator.cpp
index 91f5ec1fcb0..cbd8c1ddef4 100644
--- a/src/tools/qdoc/htmlgenerator.cpp
+++ b/src/tools/qdoc/htmlgenerator.cpp
@@ -280,6 +280,8 @@ void HtmlGenerator::generateDocs()
qflagsHref_ = linkForNode(qflags,0);
if (!preparing())
Generator::generateDocs();
+ if (Generator::generating() && Generator::writeQaPages())
+ generateQAPage();
if (!generating()) {
QString fileBase = project.toLower().simplified().replace(QLatin1Char(' '), QLatin1Char('-'));
@@ -301,6 +303,46 @@ void HtmlGenerator::generateDocs()
}
/*!
+ Output the module's Quality Assurance page.
+ */
+void HtmlGenerator::generateQAPage()
+{
+ NamespaceNode* node = qdb_->primaryTreeRoot();
+ beginSubPage(node, "aaa-" + defaultModuleName().toLower() + "-qa-page.html");
+ CodeMarker* marker = CodeMarker::markerForFileName(node->location().filePath());
+ QString title = "Quality Assurance Page for " + defaultModuleName();
+ QString t = "Quality assurance information for checking the " + defaultModuleName() + " documentation.";
+ generateHeader(title, node, marker);
+ generateTitle(title, Text() << t, LargeSubTitle, node, marker);
+
+ QStringList strings;
+ QVector<int> counts;
+ QString depends = qdb_->getLinkCounts(strings, counts);
+ if (!strings.isEmpty()) {
+ t = "Intermodule Link Counts";
+ QString ref = registerRef(t);
+ out() << "<a name=\"" << ref << "\"></a>" << divNavTop << '\n';
+ out() << "<h2 id=\"" << ref << "\">" << protectEnc(t) << "</h2>\n";
+ out() << "<table class=\"valuelist\"><tr valign=\"top\" "
+ << "class=\"even\"><th class=\"tblConst\">Destination Module</th>"
+ << "<th class=\"tblval\">Link Count</th></tr>\n";
+ for (int i = 0; i< strings.size(); ++i) {
+ out() << "<tr><td class=\"topAlign\"><tt>" << strings.at(i)
+ << "</tt></td><td class=\"topAlign\"><tt>" << counts.at(i)
+ << "</tt></td></tr>\n";
+ }
+ out() << "</table>\n";
+ t = "The Optimal \"depends\" Variable";
+ out() << "<h2>" << protectEnc(t) << "</h2>\n";
+ t = "Consider replacing the depends variable in " + defaultModuleName().toLower() +
+ ".qdocconf with this one, if the two are not identical:";
+ out() << "<p>" << protectEnc(t) << "</p>\n";
+ out() << "<p>" << protectEnc(depends) << "</p>\n";
+ }
+ endSubPage();
+}
+
+/*!
Generate html from an instance of Atom.
*/
int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMarker *marker)
@@ -3676,6 +3718,9 @@ QString HtmlGenerator::getLink(const Atom *atom, const Node *relative, const Nod
if (!(*node))
return QString();
+ if (Generator::writeQaPages())
+ qdb_->incrementLinkCount(*node);
+
QString url = (*node)->url();
if (!url.isEmpty()) {
if (ref.isEmpty())
@@ -3720,6 +3765,9 @@ QString HtmlGenerator::getAutoLink(const Atom *atom, const Node *relative, const
if (!(*node))
return QString();
+ if (Generator::writeQaPages())
+ qdb_->incrementLinkCount(*node);
+
QString url = (*node)->url();
if (!url.isEmpty()) {
if (ref.isEmpty())
diff --git a/src/tools/qdoc/htmlgenerator.h b/src/tools/qdoc/htmlgenerator.h
index 616a9893614..825522a1214 100644
--- a/src/tools/qdoc/htmlgenerator.h
+++ b/src/tools/qdoc/htmlgenerator.h
@@ -89,6 +89,7 @@ public:
static QString sinceTitle(int i) { return sinceTitles[i]; }
protected:
+ virtual void generateQAPage();
virtual int generateAtom(const Atom *atom,
const Node *relative,
CodeMarker *marker);
diff --git a/src/tools/qdoc/location.cpp b/src/tools/qdoc/location.cpp
index ca06eeb2837..923901fc349 100644
--- a/src/tools/qdoc/location.cpp
+++ b/src/tools/qdoc/location.cpp
@@ -286,6 +286,15 @@ void Location::fatal(const QString& message, const QString& details) const
}
/*!
+ Writes \a message and \a detals to stderr as a formatted
+ report message.
+ */
+void Location::report(const QString& message, const QString& details) const
+{
+ emitMessage(Report, message, details);
+}
+
+/*!
Gets several parameters from the \a config, including
tab size, program name, and a regular expression that
appears to be used for matching certain error messages
@@ -371,7 +380,8 @@ void Location::emitMessage(MessageType type,
result.prepend(tr(": error: "));
else if (type == Warning)
result.prepend(tr(": warning: "));
- result.prepend(toString());
+ if (type != Report)
+ result.prepend(toString());
fprintf(stderr, "%s\n", result.toLatin1().data());
fflush(stderr);
}
diff --git a/src/tools/qdoc/location.h b/src/tools/qdoc/location.h
index eb4dbbae982..b6589fd6ab6 100644
--- a/src/tools/qdoc/location.h
+++ b/src/tools/qdoc/location.h
@@ -81,6 +81,8 @@ public:
const QString& details = QString()) const;
void fatal(const QString& message,
const QString& details = QString()) const;
+ void report(const QString& message,
+ const QString& details = QString()) const;
static const Location null;
@@ -94,7 +96,7 @@ public:
static QString canonicalRelativePath(const QString &path);
private:
- enum MessageType { Warning, Error };
+ enum MessageType { Warning, Error, Report };
struct StackEntry
{
diff --git a/src/tools/qdoc/main.cpp b/src/tools/qdoc/main.cpp
index 79fd174a08c..186fd3195be 100644
--- a/src/tools/qdoc/main.cpp
+++ b/src/tools/qdoc/main.cpp
@@ -71,6 +71,7 @@ bool creationTimeBefore(const QFileInfo &fi1, const QFileInfo &fi2)
static bool highlighting = false;
static bool showInternal = false;
static bool singleExec = false;
+static bool writeQaPages = false;
static bool redirectDocumentationToDevNull = false;
static bool noLinkErrors = false;
static bool autolinkErrors = false;
@@ -221,6 +222,7 @@ static void processQdocconfFile(const QString &fileName)
config.setStringList(CONFIG_SYNTAXHIGHLIGHTING, QStringList(highlighting ? "true" : "false"));
config.setStringList(CONFIG_SHOWINTERNAL, QStringList(showInternal ? "true" : "false"));
config.setStringList(CONFIG_SINGLEEXEC, QStringList(singleExec ? "true" : "false"));
+ config.setStringList(CONFIG_WRITEQAPAGES, QStringList(writeQaPages ? "true" : "false"));
config.setStringList(CONFIG_REDIRECTDOCUMENTATIONTODEVNULL, QStringList(redirectDocumentationToDevNull ? "true" : "false"));
config.setStringList(CONFIG_NOLINKERRORS, QStringList(noLinkErrors ? "true" : "false"));
config.setStringList(CONFIG_AUTOLINKERRORS, QStringList(autolinkErrors ? "true" : "false"));
@@ -459,7 +461,6 @@ static void processQdocconfFile(const QString &fileName)
codeParser->doneParsingHeaderFiles();
usedParsers.clear();
- //qDebug() << "CALL: resolveInheritance()";
qdb->resolveInheritance();
/*
@@ -490,7 +491,6 @@ static void processQdocconfFile(const QString &fileName)
targets, URLs, links, and other stuff that needs resolving.
*/
Generator::debug("Resolving stuff prior to generating docs");
- //qDebug() << "CALL: resolveIssues()";
qdb->resolveIssues();
}
else {
@@ -533,7 +533,6 @@ static void processQdocconfFile(const QString &fileName)
one.
*/
Generator::debug("Generating docs");
- //qDebug() << "CALL: generateDocs()";
QSet<QString>::ConstIterator of = outputFormats.constBegin();
while (of != outputFormats.constEnd()) {
Generator* generator = Generator::generatorForFormat(*of);
@@ -543,8 +542,12 @@ static void processQdocconfFile(const QString &fileName)
generator->generateDocs();
++of;
}
+#if 0
+ if (Generator::generating() && Generator::writeQaPages())
+ qdb->printLinkCounts(project);
+#endif
+ qdb->clearLinkCounts();
- //Generator::writeOutFileNames();
Generator::debug("Terminating qdoc classes");
if (Generator::debugging())
Generator::stopDebugging(project);
@@ -681,6 +684,10 @@ int main(int argc, char **argv)
singleExecOption.setDescription(QCoreApplication::translate("qdoc", "Run qdoc once over all the qdoc conf files."));
parser.addOption(singleExecOption);
+ QCommandLineOption writeQaPagesOption(QStringList() << QStringLiteral("write-qa-pages"));
+ writeQaPagesOption.setDescription(QCoreApplication::translate("qdoc", "Write QA pages."));
+ parser.addOption(writeQaPagesOption);
+
parser.process(app);
defines += parser.values(defineOption);
@@ -688,6 +695,7 @@ int main(int argc, char **argv)
highlighting = parser.isSet(highlightingOption);
showInternal = parser.isSet(showInternalOption);
singleExec = parser.isSet(singleExecOption);
+ writeQaPages = parser.isSet(writeQaPagesOption);
redirectDocumentationToDevNull = parser.isSet(redirectDocumentationToDevNullOption);
Config::generateExamples = !parser.isSet(noExamplesOption);
foreach (const QString &indexDir, parser.values(indexDirOption)) {
@@ -713,6 +721,8 @@ int main(int argc, char **argv)
Generator::setQDocPass(Generator::Generate);
if (parser.isSet(singleExecOption))
Generator::setSingleExec();
+ if (parser.isSet(writeQaPagesOption))
+ Generator::setWriteQaPages();
if (parser.isSet(logProgressOption))
Location::startLoggingProgress();
diff --git a/src/tools/qdoc/qdocdatabase.cpp b/src/tools/qdoc/qdocdatabase.cpp
index a22244d1740..4b4c88a1766 100644
--- a/src/tools/qdoc/qdocdatabase.cpp
+++ b/src/tools/qdoc/qdocdatabase.cpp
@@ -274,7 +274,6 @@ const QVector<Tree*>& QDocForest::indexSearchOrder()
*/
NamespaceNode* QDocForest::newIndexTree(const QString& module)
{
- //qDebug() << " New index tree:" << module;
primaryTree_ = new Tree(module, qdb_);
forest_.insert(module, primaryTree_);
return primaryTree_->root();
@@ -286,7 +285,6 @@ NamespaceNode* QDocForest::newIndexTree(const QString& module)
*/
void QDocForest::newPrimaryTree(const QString& module)
{
- //qDebug() << " New primary tree:" << module;
primaryTree_ = new Tree(module, qdb_);
}
@@ -351,6 +349,61 @@ void QDocForest::mergeCollectionMaps(Node::Type nt, CNMultiMap& cnmm)
}
}
+/*!
+ Print the list of module names ordered according
+ to how many successful searches each tree had.
+ */
+void QDocForest::printLinkCounts(const QString& project)
+{
+ Location::null.report(QString("%1: Link Counts").arg(project));
+ QMultiMap<int, QString> m;
+ foreach (Tree* t, searchOrder()) {
+ if (t->linkCount() < 0)
+ m.insert(t->linkCount(), t->moduleName());
+ }
+ QString depends = "depends +=";
+ QString module = project.toLower();
+ QMultiMap<int, QString>::iterator i = m.begin();
+ while (i != m.end()) {
+ QString line = " " + i.value();
+ if (i.value() != module)
+ depends += " " + i.value();
+ int pad = 30 - line.length();
+ for (int k=0; k<pad; ++k)
+ line += " ";
+ line += "%1";
+ Location::null.report(line.arg(-(i.key())));
+ ++i;
+ }
+ Location::null.report("Optimal depends variable:");
+ Location::null.report(depends);
+}
+
+/*!
+ Print the list of module names ordered according
+ to how many successful searches each tree had.
+ */
+QString QDocForest::getLinkCounts(QStringList& strings, QVector<int>& counts)
+{
+ QMultiMap<int, QString> m;
+ foreach (Tree* t, searchOrder()) {
+ if (t->linkCount() < 0)
+ m.insert(t->linkCount(), t->moduleName());
+ }
+ QString depends = "depends +=";
+ QString module = Generator::defaultModuleName().toLower();
+ QMultiMap<int, QString>::iterator i = m.begin();
+ while (i != m.end()) {
+ if (i.value() != module) {
+ counts.append(-(i.key()));
+ strings.append(i.value());
+ depends += " " + i.value();
+ }
+ ++i;
+ }
+ return depends;
+}
+
/*! \class QDocDatabase
This class provides exclusive access to the qdoc database,
which consists of a forrest of trees and a lot of maps and
diff --git a/src/tools/qdoc/qdocdatabase.h b/src/tools/qdoc/qdocdatabase.h
index f6deeeece8d..02d39e62e75 100644
--- a/src/tools/qdoc/qdocdatabase.h
+++ b/src/tools/qdoc/qdocdatabase.h
@@ -194,6 +194,13 @@ class QDocForest
}
void clearSearchOrder() { searchOrder_.clear(); }
+ void clearLinkCounts()
+ {
+ foreach (Tree* t, searchOrder())
+ t->clearLinkCount();
+ }
+ void printLinkCounts(const QString& project);
+ QString getLinkCounts(QStringList& strings, QVector<int>& counts);
private:
void newPrimaryTree(const QString& module);
@@ -379,6 +386,12 @@ class QDocDatabase
void mergeCollections(Node::Type nt, CNMap& cnm, const Node* relative);
void mergeCollections(CollectionNode* cn);
void clearSearchOrder() { forest_.clearSearchOrder(); }
+ void incrementLinkCount(const Node* t) { t->tree()->incrementLinkCount(); }
+ void clearLinkCounts() { forest_.clearLinkCounts(); }
+ void printLinkCounts(const QString& t) { forest_.printLinkCounts(t); }
+ QString getLinkCounts(QStringList& strings, QVector<int>& counts) {
+ return forest_.getLinkCounts(strings, counts);
+ }
private:
friend class QDocIndexFiles;
diff --git a/src/tools/qdoc/tree.cpp b/src/tools/qdoc/tree.cpp
index d4169f62428..f3a6ed78da7 100644
--- a/src/tools/qdoc/tree.cpp
+++ b/src/tools/qdoc/tree.cpp
@@ -68,6 +68,7 @@ QT_BEGIN_NAMESPACE
Tree::Tree(const QString& module, QDocDatabase* qdb)
: treeHasBeenAnalyzed_(false),
docsHaveBeenGenerated_(false),
+ linkCount_(0),
module_(module),
qdb_(qdb),
root_(0, QString())
diff --git a/src/tools/qdoc/tree.h b/src/tools/qdoc/tree.h
index 5f11a81405d..75f751f9765 100644
--- a/src/tools/qdoc/tree.h
+++ b/src/tools/qdoc/tree.h
@@ -197,10 +197,14 @@ class Tree
public:
const QString& moduleName() const { return module_; }
const QString& indexFileName() const { return indexFileName_; }
+ void incrementLinkCount() { --linkCount_; }
+ void clearLinkCount() { linkCount_ = 0; }
+ int linkCount() const { return linkCount_; }
private:
bool treeHasBeenAnalyzed_;
bool docsHaveBeenGenerated_;
+ int linkCount_;
QString module_;
QString indexFileName_;
QDocDatabase* qdb_;