summaryrefslogtreecommitdiffstats
path: root/src/tools
diff options
context:
space:
mode:
authorMartin Smith <martin.smith@digia.com>2014-10-01 11:48:29 +0200
committerMartin Smith <martin.smith@digia.com>2014-10-17 10:42:56 +0200
commit77165553af2e541ca01ec6bf47461ba910d9519b (patch)
tree758ded5c640807938800f2efde7d6165b1227b83 /src/tools
parent880986be2357a1f80827d038d770dc2f80300201 (diff)
qdoc: qdoc was too slow
The hard-coded search order is now removed. The search order is now constructed from the depends variable in the qdocconf file. The basic idea is that qdoc is run once. It gets a list of all the qdocconf files for the modules in Qt5. First, qdoc runs in -prepare mode for each qdocconf file in the list. It generates the index file for each module, but these index files are never used. At the end of the -prepare phase for each module, qdoc keeps the tree structure for the module in a collection of trees. Second, qdoc runs in -generate mode for each qdocconf file in the list. But now it uses the existing tree for that module, so it doesn't have to read the sources files again, and it doesn't have to read any index files. Now it generates the docs for each module. The runtime for qdoc has been reduced by 90% when running qdoc for all of Qt5 on a not so new iMac. Before this update, qdoc took about 10 minutes to generate docs for Qt5. Now it takes a little over 1 minute. The new way to run qdoc is described in the Qt bug report referenced here. Note that running qdoc this new (old) way also generates fewer qdoc errors than when running qdoc the old way. This indicates that the index files qdoc uses when running the old way are incomplete. Note also that the old way of running qdoc is not affected by this update. The old way is still required for running qdoc in the current qmake/make system. That process must be changed to be able to use the faster qdoc. The details are provided in the Qt bug report. Change-Id: Ibec41d6fbaa9fc8cd070a05d04357bd02c4478f0 Task-number: QTBUG-41705 Reviewed-by: Topi Reiniƶ <topi.reinio@digia.com>
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/qdoc/codeparser.cpp9
-rw-r--r--src/tools/qdoc/codeparser.h5
-rw-r--r--src/tools/qdoc/config.cpp35
-rw-r--r--src/tools/qdoc/config.h3
-rw-r--r--src/tools/qdoc/ditaxmlgenerator.cpp6
-rw-r--r--src/tools/qdoc/doc.cpp15
-rw-r--r--src/tools/qdoc/generator.cpp10
-rw-r--r--src/tools/qdoc/generator.h14
-rw-r--r--src/tools/qdoc/htmlgenerator.cpp40
-rw-r--r--src/tools/qdoc/location.cpp4
-rw-r--r--src/tools/qdoc/main.cpp477
-rw-r--r--src/tools/qdoc/node.cpp2
-rw-r--r--src/tools/qdoc/qdocdatabase.cpp268
-rw-r--r--src/tools/qdoc/qdocdatabase.h32
-rw-r--r--src/tools/qdoc/qdocindexfiles.cpp4
-rw-r--r--src/tools/qdoc/tokenizer.cpp8
-rw-r--r--src/tools/qdoc/tree.cpp6
-rw-r--r--src/tools/qdoc/tree.h10
18 files changed, 564 insertions, 384 deletions
diff --git a/src/tools/qdoc/codeparser.cpp b/src/tools/qdoc/codeparser.cpp
index 00341940da..acb297d5f9 100644
--- a/src/tools/qdoc/codeparser.cpp
+++ b/src/tools/qdoc/codeparser.cpp
@@ -65,9 +65,9 @@ QT_BEGIN_NAMESPACE
#define COMMAND_TITLE Doc::alias(QLatin1String("title"))
#define COMMAND_WRAPPER Doc::alias(QLatin1String("wrapper"))
-QString CodeParser::currentSubDir_;
QList<CodeParser *> CodeParser::parsers;
-bool CodeParser::showInternal = false;
+bool CodeParser::showInternal_ = false;
+bool CodeParser::singleExec_ = false;
/*!
The constructor adds this code parser to the static
@@ -93,7 +93,8 @@ CodeParser::~CodeParser()
*/
void CodeParser::initializeParser(const Config& config)
{
- showInternal = config.getBool(CONFIG_SHOWINTERNAL);
+ showInternal_ = config.getBool(CONFIG_SHOWINTERNAL);
+ singleExec_ = config.getBool(CONFIG_SINGLEEXEC);
}
/*!
@@ -262,7 +263,7 @@ void CodeParser::processCommonMetaCommand(const Location& location,
node->setStatus(Node::Preliminary);
}
else if (command == COMMAND_INTERNAL) {
- if (!showInternal) {
+ if (!showInternal_) {
node->setAccess(Node::Private);
node->setStatus(Node::Internal);
if (node->type() == Node::QmlPropertyGroup) {
diff --git a/src/tools/qdoc/codeparser.h b/src/tools/qdoc/codeparser.h
index 5b3b1192f3..c9a9b746b1 100644
--- a/src/tools/qdoc/codeparser.h
+++ b/src/tools/qdoc/codeparser.h
@@ -74,7 +74,6 @@ public:
static CodeParser *parserForHeaderFile(const QString &filePath);
static CodeParser *parserForSourceFile(const QString &filePath);
static void setLink(Node* node, Node::LinkType linkType, const QString& arg);
- static const QString& currentOutputSubdirectory() { return currentSubDir_; }
protected:
const QSet<QString>& commonMetaCommands();
@@ -89,9 +88,9 @@ protected:
QDocDatabase* qdb_;
private:
- static QString currentSubDir_;
static QList<CodeParser *> parsers;
- static bool showInternal;
+ static bool showInternal_;
+ static bool singleExec_;
};
QT_END_NAMESPACE
diff --git a/src/tools/qdoc/config.cpp b/src/tools/qdoc/config.cpp
index 51ab341869..bd72fc106b 100644
--- a/src/tools/qdoc/config.cpp
+++ b/src/tools/qdoc/config.cpp
@@ -98,6 +98,7 @@ QString ConfigStrings::QUOTINGINFORMATION = QStringLiteral("quotinginformation")
QString ConfigStrings::SCRIPTDIRS = QStringLiteral("scriptdirs");
QString ConfigStrings::SCRIPTS = QStringLiteral("scripts");
QString ConfigStrings::SHOWINTERNAL = QStringLiteral("showinternal");
+QString ConfigStrings::SINGLEEXEC = QStringLiteral("singleexec");
QString ConfigStrings::SOURCEDIRS = QStringLiteral("sourcedirs");
QString ConfigStrings::SOURCEENCODING = QStringLiteral("sourceencoding");
QString ConfigStrings::SOURCES = QStringLiteral("sources");
@@ -350,6 +351,10 @@ QString Config::getOutputDir() const
t = getString(CONFIG_OUTPUTDIR);
else
t = overrideOutputDir;
+ if (Generator::singleExec()) {
+ QString project = getString(CONFIG_PROJECT);
+ t += QLatin1Char('/') + project.toLower();
+ }
if (!Generator::useOutputSubdirs()) {
t = t.left(t.lastIndexOf('/'));
QString singleOutputSubdir = getString("HTML.outputsubdir");
@@ -869,6 +874,36 @@ bool Config::isMetaKeyChar(QChar ch)
}
/*!
+ \a fileName is a master qdocconf file. It contains a list of
+ qdocconf files and nothing else. Read the list and return it.
+ */
+QStringList Config::loadMaster(const QString& fileName)
+{
+ Location location = Location::null;
+ QFile fin(fileName);
+ if (!fin.open(QFile::ReadOnly | QFile::Text)) {
+ if (!Config::installDir.isEmpty()) {
+ int prefix = location.filePath().length() - location.fileName().length();
+ fin.setFileName(Config::installDir + "/" + fileName.right(fileName.length() - prefix));
+ }
+ if (!fin.open(QFile::ReadOnly | QFile::Text))
+ location.fatal(tr("Cannot open master qdocconf file '%1': %2").arg(fileName).arg(fin.errorString()));
+ }
+ QTextStream stream(&fin);
+#ifndef QT_NO_TEXTCODEC
+ stream.setCodec("UTF-8");
+#endif
+ QStringList qdocFiles;
+ QString line = stream.readLine();
+ while (!line.isNull()) {
+ qdocFiles.append(line);
+ line = stream.readLine();
+ }
+ fin.close();
+ return qdocFiles;
+}
+
+/*!
Load, parse, and process a qdoc configuration file. This
function is only called by the other load() function, but
this one is recursive, i.e., it calls itself when it sees
diff --git a/src/tools/qdoc/config.h b/src/tools/qdoc/config.h
index 70b5adfd68..c4d4f27a9e 100644
--- a/src/tools/qdoc/config.h
+++ b/src/tools/qdoc/config.h
@@ -108,6 +108,7 @@ public:
QStringList getExampleQdocFiles(const QSet<QString> &excludedDirs, const QSet<QString> &excludedFiles);
QStringList getExampleImageFiles(const QSet<QString> &excludedDirs, const QSet<QString> &excludedFiles);
+ static QStringList loadMaster(const QString& fileName);
static QStringList getFilesHere(const QString& dir,
const QString& nameFilter,
const Location &location = Location(),
@@ -209,6 +210,7 @@ struct ConfigStrings
static QString SCRIPTDIRS;
static QString SCRIPTS;
static QString SHOWINTERNAL;
+ static QString SINGLEEXEC;
static QString SOURCEDIRS;
static QString SOURCEENCODING;
static QString SOURCES;
@@ -282,6 +284,7 @@ struct ConfigStrings
#define CONFIG_SCRIPTDIRS ConfigStrings::SCRIPTDIRS
#define CONFIG_SCRIPTS ConfigStrings::SCRIPTS
#define CONFIG_SHOWINTERNAL ConfigStrings::SHOWINTERNAL
+#define CONFIG_SINGLEEXEC ConfigStrings::SINGLEEXEC
#define CONFIG_SOURCEDIRS ConfigStrings::SOURCEDIRS
#define CONFIG_SOURCEENCODING ConfigStrings::SOURCEENCODING
#define CONFIG_SOURCES ConfigStrings::SOURCES
diff --git a/src/tools/qdoc/ditaxmlgenerator.cpp b/src/tools/qdoc/ditaxmlgenerator.cpp
index d9cf56769b..54b358e170 100644
--- a/src/tools/qdoc/ditaxmlgenerator.cpp
+++ b/src/tools/qdoc/ditaxmlgenerator.cpp
@@ -663,10 +663,10 @@ GuidMap* DitaXmlGenerator::lookupGuidMap(const QString& fileName)
*/
void DitaXmlGenerator::generateDocs()
{
- if (!runPrepareOnly())
+ if (!preparing())
Generator::generateDocs();
- if (!runGenerateOnly()) {
+ if (!generating()) {
QString fileBase = project.toLower().simplified().replace(QLatin1Char(' '), QLatin1Char('-'));
qdb_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index",
projectUrl,
@@ -675,7 +675,7 @@ void DitaXmlGenerator::generateDocs()
true);
}
- if (!runPrepareOnly()) {
+ if (!preparing()) {
writeDitaMap();
/*
Generate the XML tag file, if it was requested.
diff --git a/src/tools/qdoc/doc.cpp b/src/tools/qdoc/doc.cpp
index 5a3ad959d2..cdb8472e4f 100644
--- a/src/tools/qdoc/doc.cpp
+++ b/src/tools/qdoc/doc.cpp
@@ -2816,18 +2816,6 @@ QString DocParser::slashed(const QString& str)
#define COMMAND_BRIEF Doc::alias("brief")
#define COMMAND_QMLBRIEF Doc::alias("qmlbrief")
-#if 0
-Doc::Doc(const Location& start_loc,
- const Location& end_loc,
- const QString& source,
- const QSet<QString>& metaCommandSet)
-{
- priv = new DocPrivate(start_loc,end_loc,source);
- DocParser parser;
- parser.parse(source,priv,metaCommandSet,QSet<QString>());
-}
-#endif
-
/*!
Parse the qdoc comment \a source. Build up a list of all the topic
commands found including their arguments. This constructor is used
@@ -3234,6 +3222,9 @@ void Doc::initialize(const Config& config)
}
}
+/*!
+ All the heap allocated variables are deleted.
+ */
void Doc::terminate()
{
DocParser::exampleFiles.clear();
diff --git a/src/tools/qdoc/generator.cpp b/src/tools/qdoc/generator.cpp
index 5aff19e121..6f53783aaf 100644
--- a/src/tools/qdoc/generator.cpp
+++ b/src/tools/qdoc/generator.cpp
@@ -92,7 +92,8 @@ bool Generator::debugging_ = false;
bool Generator::noLinkErrors_ = false;
bool Generator::autolinkErrors_ = false;
bool Generator::redirectDocumentationToDevNull_ = false;
-Generator::Passes Generator::qdocPass_ = Both;
+Generator::QDocPass Generator::qdocPass_ = Generator::Neither;
+bool Generator::qdocSingleExec_ = false;
bool Generator::useOutputSubdirs_ = true;
void Generator::startDebugging(const QString& message)
@@ -134,6 +135,7 @@ Generator::Generator()
inTableHeader_(false),
threeColumnEnumValueTable_(true),
showInternal_(false),
+ singleExec_(false),
numTableRows_(0)
{
qdb_ = QDocDatabase::qdocDB();
@@ -259,7 +261,8 @@ void Generator::writeOutFileNames()
void Generator::beginSubPage(const InnerNode* node, const QString& fileName)
{
QString path = outputDir() + QLatin1Char('/');
- if (Generator::useOutputSubdirs() && !node->outputSubdirectory().isEmpty())
+ if (Generator::useOutputSubdirs() && !node->outputSubdirectory().isEmpty() &&
+ !outputDir().endsWith(node->outputSubdirectory()))
path += node->outputSubdirectory() + QLatin1Char('/');
path += fileName;
@@ -1529,7 +1532,7 @@ void Generator::initialize(const Config &config)
QDir dirInfo;
if (dirInfo.exists(outDir_)) {
- if (!runGenerateOnly() && Generator::useOutputSubdirs()) {
+ if (!generating() && Generator::useOutputSubdirs()) {
if (!Config::removeDirContents(outDir_))
config.lastLocation().error(tr("Cannot empty output directory '%1'").arg(outDir_));
}
@@ -1678,6 +1681,7 @@ void Generator::initializeGenerator(const Config& config)
{
config_ = &config;
showInternal_ = config.getBool(CONFIG_SHOWINTERNAL);
+ singleExec_ = config.getBool(CONFIG_SINGLEEXEC);
}
bool Generator::matchAhead(const Atom *atom, Atom::Type expectedAtomType)
diff --git a/src/tools/qdoc/generator.h b/src/tools/qdoc/generator.h
index 110a8d9e73..306a1596ae 100644
--- a/src/tools/qdoc/generator.h
+++ b/src/tools/qdoc/generator.h
@@ -61,7 +61,7 @@ class Generator
Q_DECLARE_TR_FUNCTIONS(QDoc::Generator)
public:
- enum Passes { Both, Prepare, Generate };
+ enum QDocPass { Neither, Prepare, Generate };
enum ListType { Generic, Obsolete };
Generator();
@@ -91,9 +91,11 @@ public:
static bool debugging() { return debugging_; }
static bool noLinkErrors() { return noLinkErrors_; }
static bool autolinkErrors() { return autolinkErrors_; }
- static void setQDocPass(Passes pass) { qdocPass_ = pass; }
- static bool runPrepareOnly() { return (qdocPass_ == Prepare); }
- static bool runGenerateOnly() { return (qdocPass_ == Generate); }
+ static void setQDocPass(QDocPass t) { qdocPass_ = t; }
+ static bool preparing() { return (qdocPass_ == Prepare); }
+ static bool generating() { return (qdocPass_ == Generate); }
+ static bool singleExec() { return qdocSingleExec_; }
+ static void setSingleExec() { qdocSingleExec_ = true; }
static QString defaultModuleName() { return project; }
static void resetUseOutputSubdirs() { useOutputSubdirs_ = false; }
static bool useOutputSubdirs() { return useOutputSubdirs_; }
@@ -212,7 +214,8 @@ private:
static bool noLinkErrors_;
static bool autolinkErrors_;
static bool redirectDocumentationToDevNull_;
- static Passes qdocPass_;
+ static QDocPass qdocPass_;
+ static bool qdocSingleExec_;
static bool useOutputSubdirs_;
void generateReimplementedFrom(const FunctionNode *func, CodeMarker *marker);
@@ -232,6 +235,7 @@ private:
bool inTableHeader_;
bool threeColumnEnumValueTable_;
bool showInternal_;
+ bool singleExec_;
int numTableRows_;
QString link_;
QString sectionNumber_;
diff --git a/src/tools/qdoc/htmlgenerator.cpp b/src/tools/qdoc/htmlgenerator.cpp
index 32399f11c7..91f5ec1fcb 100644
--- a/src/tools/qdoc/htmlgenerator.cpp
+++ b/src/tools/qdoc/htmlgenerator.cpp
@@ -100,8 +100,10 @@ HtmlGenerator::HtmlGenerator()
*/
HtmlGenerator::~HtmlGenerator()
{
- if (helpProjectWriter)
+ if (helpProjectWriter) {
delete helpProjectWriter;
+ helpProjectWriter = 0;
+ }
}
/*!
@@ -130,6 +132,11 @@ void HtmlGenerator::initializeGenerator(const Config &config)
Generator::initializeGenerator(config);
obsoleteLinks = config.getBool(CONFIG_OBSOLETELINKS);
setImageFileExtensions(QStringList() << "png" << "jpg" << "jpeg" << "gif");
+
+ /*
+ The formatting maps are owned by Generator. They are cleared in
+ Generator::terminate().
+ */
int i = 0;
while (defaults[i].key) {
formattingLeftMap().insert(defaults[i].key, defaults[i].left);
@@ -211,7 +218,12 @@ void HtmlGenerator::initializeGenerator(const Config &config)
// The following line was changed to fix QTBUG-27798
//codeIndent = config.getInt(CONFIG_CODEINDENT);
- helpProjectWriter = new HelpProjectWriter(config, project.toLower() + ".qhp", this);
+ /*
+ The help file write should be allocated once and only once
+ per qdoc execution.
+ */
+ if (helpProjectWriter == 0)
+ helpProjectWriter = new HelpProjectWriter(config, project.toLower() + ".qhp", this);
// Documentation template handling
headerScripts = config.getString(HtmlGenerator::format() + Config::dot + CONFIG_HEADERSCRIPTS);
@@ -266,10 +278,10 @@ void HtmlGenerator::generateDocs()
Node* qflags = qdb_->findClassNode(QStringList("QFlags"));
if (qflags)
qflagsHref_ = linkForNode(qflags,0);
- if (!runPrepareOnly())
+ if (!preparing())
Generator::generateDocs();
- if (!runGenerateOnly()) {
+ if (!generating()) {
QString fileBase = project.toLower().simplified().replace(QLatin1Char(' '), QLatin1Char('-'));
qdb_->generateIndex(outputDir() + QLatin1Char('/') + fileBase + ".index",
projectUrl,
@@ -278,7 +290,7 @@ void HtmlGenerator::generateDocs()
true);
}
- if (!runPrepareOnly()) {
+ if (!preparing()) {
helpProjectWriter->generate();
generateManifestFiles();
/*
@@ -2313,7 +2325,7 @@ QString HtmlGenerator::generateListOfAllMemberFile(const InnerNode *inner,
out() << ", including inherited members.</p>\n";
Section section = sections.first();
- generateSectionList(section, 0, marker, CodeMarker::Subpage);
+ generateSectionList(section, inner, marker, CodeMarker::Subpage);
generateFooter();
endSubPage();
@@ -2369,7 +2381,7 @@ QString HtmlGenerator::generateAllQmlMembersFile(QmlClassNode* qml_cn, CodeMarke
prefix = keys.at(j).mid(1);
prefix = prefix.left(keys.at(j).indexOf("::")+1);
}
- generateQmlItem(nodes[j], qcn, marker, true);
+ generateQmlItem(nodes[j], qml_cn, marker, true);
if (nodes[j]->isAttached())
out() << " [attached]";
//generateSynopsis(nodes[j], qcn, marker, CodeMarker::Subpage, false, &prefix);
@@ -2796,8 +2808,9 @@ void HtmlGenerator::generateCompactList(ListType listType,
else if (listType == Obsolete) {
QString fileName = fileBase(it.value()) + "-obsolete." + fileExtension();
QString link;
- if (useOutputSubdirs())
+ if (useOutputSubdirs()) {
link = QString("../" + it.value()->outputSubdirectory() + QLatin1Char('/'));
+ }
link += fileName;
out() << "<a href=\"" << link << "\">";
}
@@ -2837,7 +2850,7 @@ void HtmlGenerator::generateFunctionIndex(const Node *relative)
char currentLetter;
out() << "<ul>\n";
- NodeMapMap funcIndex = qdb_->getFunctionIndex();
+ NodeMapMap& funcIndex = qdb_->getFunctionIndex();
QMap<QString, NodeMap >::ConstIterator f = funcIndex.constBegin();
while (f != funcIndex.constEnd()) {
out() << "<li>";
@@ -3723,7 +3736,6 @@ QString HtmlGenerator::getAutoLink(const Atom *atom, const Node *relative, const
return link;
}
-
/*!
Construct the link string for the \a node and return it.
The \a relative node is use to decide the link we are
@@ -3778,7 +3790,10 @@ QString HtmlGenerator::linkForNode(const Node *node, const Node *relative)
if (node && relative && (node != relative)) {
if (useOutputSubdirs() && !node->isExternalPage() &&
node->outputSubdirectory() != relative->outputSubdirectory()) {
- link.prepend(QString("../" + node->outputSubdirectory() + QLatin1Char('/')));
+ if (link.startsWith(node->outputSubdirectory()))
+ link.prepend(QString("../"));
+ else
+ link.prepend(QString("../" + node->outputSubdirectory() + QLatin1Char('/')));
}
}
return link;
@@ -4507,6 +4522,9 @@ void HtmlGenerator::generateManifestFile(QString manifest, QString element)
Reads metacontent - additional attributes and tags to apply
when generating manifest files, read from config. Takes the
configuration class \a config as a parameter.
+
+ The manifest metacontent map is cleared immediately after
+ the manifest files have been generated.
*/
void HtmlGenerator::readManifestMetaContent(const Config &config)
{
diff --git a/src/tools/qdoc/location.cpp b/src/tools/qdoc/location.cpp
index 040dd0cd88..ca06eeb283 100644
--- a/src/tools/qdoc/location.cpp
+++ b/src/tools/qdoc/location.cpp
@@ -256,7 +256,7 @@ QString Location::canonicalRelativePath(const QString &path)
*/
void Location::warning(const QString& message, const QString& details) const
{
- if (!Generator::runPrepareOnly())
+ if (!Generator::preparing())
emitMessage(Warning, message, details);
}
@@ -267,7 +267,7 @@ void Location::warning(const QString& message, const QString& details) const
*/
void Location::error(const QString& message, const QString& details) const
{
- if (!Generator::runPrepareOnly())
+ if (!Generator::preparing())
emitMessage(Error, message, details);
}
diff --git a/src/tools/qdoc/main.cpp b/src/tools/qdoc/main.cpp
index 118c206f16..79fd174a08 100644
--- a/src/tools/qdoc/main.cpp
+++ b/src/tools/qdoc/main.cpp
@@ -63,7 +63,6 @@
QT_BEGIN_NAMESPACE
-
bool creationTimeBefore(const QFileInfo &fi1, const QFileInfo &fi2)
{
return fi1.lastModified() < fi2.lastModified();
@@ -71,6 +70,7 @@ bool creationTimeBefore(const QFileInfo &fi1, const QFileInfo &fi2)
static bool highlighting = false;
static bool showInternal = false;
+static bool singleExec = false;
static bool redirectDocumentationToDevNull = false;
static bool noLinkErrors = false;
static bool autolinkErrors = false;
@@ -80,14 +80,22 @@ static QStringList dependModules;
static QStringList indexDirs;
static QString currentDir;
static QString prevCurrentDir;
+static QHash<QString,QString> defaults;
+#ifndef QT_NO_TRANSLATION
+typedef QPair<QString, QTranslator*> Translator;
+static QList<Translator> translators;
+#endif
-
+/*!
+ Read some XML indexes containing definitions from other
+ documentation sets. \a config contains a variable that
+ lists directories where index files can bge found. It also
+ contains the \c depends variable, which lists the modules
+ that the current module depends on.
+*/
static void loadIndexFiles(Config& config)
{
QDocDatabase* qdb = QDocDatabase::qdocDB();
- /*
- Read some XML indexes containing definitions from other documentation sets.
- */
QStringList indexFiles;
QStringList configIndexes = config.getStringList(CONFIG_INDEXES);
foreach (const QString &index, configIndexes) {
@@ -194,35 +202,17 @@ static void loadIndexFiles(Config& config)
*/
static void processQdocconfFile(const QString &fileName)
{
-#ifndef QT_NO_TRANSLATION
- QList<QTranslator *> translators;
-#endif
-
/*
The Config instance represents the configuration data for qdoc.
- All the other classes are initialized with the config. Here we
+ All the other classes are initialized with the config. Below, we
initialize the configuration with some default values.
- */
- Config config(QCoreApplication::translate("QDoc", "qdoc"));
- /*
- The default indent for code is 4.
- The default value for false is 0.
- The default supported file extensions are cpp, h, qdoc and qml.
- The default language is c++.
- The default output format is html.
- The default tab size is 8.
- And those are all the default values for configuration variables.
+ I don't think the call to translate() does anything here. For one
+ thing, the translators haven't been installed at this point. And
+ I doubt any translator would translate QDoc anyway. But I left it
+ here because it does no harm.
*/
- static QHash<QString,QString> defaults;
- if (defaults.isEmpty()) {
- defaults.insert(CONFIG_CODEINDENT, QLatin1String("4"));
- defaults.insert(CONFIG_FALSEHOODS, QLatin1String("0"));
- defaults.insert(CONFIG_FILEEXTENSIONS, QLatin1String("*.cpp *.h *.qdoc *.qml"));
- defaults.insert(CONFIG_LANGUAGE, QLatin1String("Cpp"));
- defaults.insert(CONFIG_OUTPUTFORMATS, QLatin1String("HTML"));
- defaults.insert(CONFIG_TABSIZE, QLatin1String("8"));
- }
+ Config config(QCoreApplication::translate("QDoc", "qdoc"));
QHash<QString,QString>::iterator iter;
for (iter = defaults.begin(); iter != defaults.end(); ++iter)
@@ -230,6 +220,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_REDIRECTDOCUMENTATIONTODEVNULL, QStringList(redirectDocumentationToDevNull ? "true" : "false"));
config.setStringList(CONFIG_NOLINKERRORS, QStringList(noLinkErrors ? "true" : "false"));
config.setStringList(CONFIG_AUTOLINKERRORS, QStringList(autolinkErrors ? "true" : "false"));
@@ -247,8 +238,8 @@ static void processQdocconfFile(const QString &fileName)
currentDir = QFileInfo(fileName).path();
Location::initialize(config);
config.load(fileName);
- QString project = config.getString(CONFIG_PROJECT).toLower();
- //qDebug() << "\nStart project:" << project;
+ QString project = config.getString(CONFIG_PROJECT);
+ //qDebug() << "Start project:" << project;
/*
Add the defines to the configuration variables.
*/
@@ -261,17 +252,24 @@ static void processQdocconfFile(const QString &fileName)
if (!currentDir.isEmpty())
QDir::setCurrent(currentDir);
- QString phase;
- if (Generator::runPrepareOnly())
- phase = " in -prepare mode ";
- else if (Generator::runGenerateOnly())
- phase = " in -generate mode ";
+ QString phase = " in -";
+ if (Generator::singleExec())
+ phase += "single exec mode, ";
+ else
+ phase += "separate exec mode, ";
+ if (Generator::preparing())
+ phase += "prepare phase ";
+ else if (Generator::generating())
+ phase += "generate phase ";
QString msg = "Running qdoc for " + config.getString(CONFIG_PROJECT) + phase;
Location::logToStdErr(msg);
/*
Initialize all the classes and data structures with the
- qdoc configuration.
+ qdoc configuration. This is safe to do for each qdocconf
+ file processed, because all the data structures created
+ are either cleared after they have been used, or they
+ are cleared in the terminate() functions below.
*/
Location::initialize(config);
Tokenizer::initialize(config);
@@ -282,16 +280,32 @@ static void processQdocconfFile(const QString &fileName)
#ifndef QT_NO_TRANSLATION
/*
- Load the language translators, if the configuration specifies any.
+ Load the language translators, if the configuration specifies any,
+ but only if they haven't already been loaded. This works in both
+ -prepare/-generate mode and -singleexec mode.
*/
QStringList fileNames = config.getStringList(CONFIG_TRANSLATORS);
QStringList::ConstIterator fn = fileNames.constBegin();
while (fn != fileNames.constEnd()) {
- QTranslator *translator = new QTranslator(0);
- if (!translator->load(*fn))
- config.lastLocation().error(QCoreApplication::translate("QDoc", "Cannot load translator '%1'").arg(*fn));
- QCoreApplication::instance()->installTranslator(translator);
- translators.append(translator);
+ bool found = false;
+ if (!translators.isEmpty()) {
+ for (int i=0; i<translators.size(); ++i) {
+ if (translators.at(i).first == *fn) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ QTranslator *translator = new QTranslator(0);
+ if (!translator->load(*fn)) {
+ config.lastLocation().error(QCoreApplication::translate("QDoc", "Cannot load translator '%1'").arg(*fn));
+ }
+ else {
+ QCoreApplication::instance()->installTranslator(translator);
+ translators.append(Translator(*fn, translator));
+ }
+ }
++fn;
}
#endif
@@ -311,175 +325,227 @@ static void processQdocconfFile(const QString &fileName)
will be stored. The database includes a tree of nodes, which gets
built as the source files are parsed. The documentation output is
generated by traversing that tree.
+
+ Note: qdocDB() allocates a new instance only if no instance exists.
+ So it is safe to call qdocDB() any time.
*/
QDocDatabase* qdb = QDocDatabase::qdocDB();
qdb->setVersion(config.getString(CONFIG_VERSION));
qdb->setShowInternal(config.getBool(CONFIG_SHOWINTERNAL));
+ qdb->setSingleExec(config.getBool(CONFIG_SINGLEEXEC));
/*
By default, the only output format is HTML.
*/
QSet<QString> outputFormats = config.getOutputFormats();
Location outputFormatsLocation = config.lastLocation();
- //if (!Generator::runPrepareOnly())
- Generator::debug(" loading index files");
- loadIndexFiles(config);
- qdb->newPrimaryTree(config.getString(CONFIG_PROJECT));
- qdb->setSearchOrder();
- Generator::debug(" done loading index files");
+ qdb->clearSearchOrder();
+ QString p = config.getString(CONFIG_PROJECT).toLower();
+ if (!Generator::singleExec()) {
+ Generator::debug(" loading index files");
+ loadIndexFiles(config);
+ Generator::debug(" done loading index files");
+ qdb->newPrimaryTree(p);
+ }
+ else if (Generator::preparing())
+ qdb->newPrimaryTree(p);
+ else
+ qdb->setPrimaryTree(p);
+
+ dependModules = config.getStringList(CONFIG_DEPENDS);
+ dependModules.removeDuplicates();
+ qdb->setSearchOrder(dependModules);
QSet<QString> excludedDirs;
QSet<QString> excludedFiles;
- QStringList headerList;
- QStringList sourceList;
QStringList excludedDirsList;
QStringList excludedFilesList;
- Generator::debug("Reading excludedirs");
- excludedDirsList = config.getCanonicalPathList(CONFIG_EXCLUDEDIRS);
- foreach (const QString &excludeDir, excludedDirsList) {
- QString p = QDir::fromNativeSeparators(excludeDir);
- QDir tmp(p);
- if (tmp.exists())
- excludedDirs.insert(p);
- }
+ if (!Generator::singleExec() || !Generator::generating()) {
+ QStringList headerList;
+ QStringList sourceList;
+
+ Generator::debug("Reading excludedirs");
+ excludedDirsList = config.getCanonicalPathList(CONFIG_EXCLUDEDIRS);
+ foreach (const QString &excludeDir, excludedDirsList) {
+ QString p = QDir::fromNativeSeparators(excludeDir);
+ QDir tmp(p);
+ if (tmp.exists())
+ excludedDirs.insert(p);
+ }
- Generator::debug("Reading excludefiles");
- excludedFilesList = config.getCanonicalPathList(CONFIG_EXCLUDEFILES);
- foreach (const QString& excludeFile, excludedFilesList) {
- QString p = QDir::fromNativeSeparators(excludeFile);
- excludedFiles.insert(p);
- }
+ Generator::debug("Reading excludefiles");
+ excludedFilesList = config.getCanonicalPathList(CONFIG_EXCLUDEFILES);
+ foreach (const QString& excludeFile, excludedFilesList) {
+ QString p = QDir::fromNativeSeparators(excludeFile);
+ excludedFiles.insert(p);
+ }
- Generator::debug("Reading headerdirs");
- headerList = config.getAllFiles(CONFIG_HEADERS,CONFIG_HEADERDIRS,excludedDirs,excludedFiles);
- QMap<QString,QString> headers;
- QMultiMap<QString,QString> headerFileNames;
- for (int i=0; i<headerList.size(); ++i) {
- if (headerList[i].contains(QString("doc/snippets")))
- continue;
- if (headers.contains(headerList[i]))
- continue;
- headers.insert(headerList[i],headerList[i]);
- QString t = headerList[i].mid(headerList[i].lastIndexOf('/')+1);
- headerFileNames.insert(t,t);
- }
+ Generator::debug("Reading headerdirs");
+ headerList = config.getAllFiles(CONFIG_HEADERS,CONFIG_HEADERDIRS,excludedDirs,excludedFiles);
+ QMap<QString,QString> headers;
+ QMultiMap<QString,QString> headerFileNames;
+ for (int i=0; i<headerList.size(); ++i) {
+ if (headerList[i].contains(QString("doc/snippets")))
+ continue;
+ if (headers.contains(headerList[i]))
+ continue;
+ headers.insert(headerList[i],headerList[i]);
+ QString t = headerList[i].mid(headerList[i].lastIndexOf('/')+1);
+ headerFileNames.insert(t,t);
+ }
- Generator::debug("Reading sourcedirs");
- sourceList = config.getAllFiles(CONFIG_SOURCES,CONFIG_SOURCEDIRS,excludedDirs,excludedFiles);
- QMap<QString,QString> sources;
- QMultiMap<QString,QString> sourceFileNames;
- for (int i=0; i<sourceList.size(); ++i) {
- if (sourceList[i].contains(QString("doc/snippets")))
- continue;
- if (sources.contains(sourceList[i]))
- continue;
- sources.insert(sourceList[i],sourceList[i]);
- QString t = sourceList[i].mid(sourceList[i].lastIndexOf('/')+1);
- sourceFileNames.insert(t,t);
- }
- /*
- Find all the qdoc files in the example dirs, and add
- them to the source files to be parsed.
- */
- Generator::debug("Reading exampledirs");
- QStringList exampleQdocList = config.getExampleQdocFiles(excludedDirs, excludedFiles);
- for (int i=0; i<exampleQdocList.size(); ++i) {
- if (!sources.contains(exampleQdocList[i])) {
- sources.insert(exampleQdocList[i],exampleQdocList[i]);
- QString t = exampleQdocList[i].mid(exampleQdocList[i].lastIndexOf('/')+1);
+ Generator::debug("Reading sourcedirs");
+ sourceList = config.getAllFiles(CONFIG_SOURCES,CONFIG_SOURCEDIRS,excludedDirs,excludedFiles);
+ QMap<QString,QString> sources;
+ QMultiMap<QString,QString> sourceFileNames;
+ for (int i=0; i<sourceList.size(); ++i) {
+ if (sourceList[i].contains(QString("doc/snippets")))
+ continue;
+ if (sources.contains(sourceList[i]))
+ continue;
+ sources.insert(sourceList[i],sourceList[i]);
+ QString t = sourceList[i].mid(sourceList[i].lastIndexOf('/')+1);
sourceFileNames.insert(t,t);
}
- }
+ /*
+ Find all the qdoc files in the example dirs, and add
+ them to the source files to be parsed.
+ */
+ Generator::debug("Reading exampledirs");
+ QStringList exampleQdocList = config.getExampleQdocFiles(excludedDirs, excludedFiles);
+ for (int i=0; i<exampleQdocList.size(); ++i) {
+ if (!sources.contains(exampleQdocList[i])) {
+ sources.insert(exampleQdocList[i],exampleQdocList[i]);
+ QString t = exampleQdocList[i].mid(exampleQdocList[i].lastIndexOf('/')+1);
+ sourceFileNames.insert(t,t);
+ }
+ }
- Generator::debug("Adding doc/image dirs found in exampledirs to imagedirs");
- QSet<QString> exampleImageDirs;
- QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles);
- for (int i=0; i<exampleImageList.size(); ++i) {
- if (exampleImageList[i].contains("doc/images")) {
- QString t = exampleImageList[i].left(exampleImageList[i].lastIndexOf("doc/images")+10);
- if (!exampleImageDirs.contains(t)) {
- exampleImageDirs.insert(t);
+ Generator::debug("Adding doc/image dirs found in exampledirs to imagedirs");
+ QSet<QString> exampleImageDirs;
+ QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles);
+ for (int i=0; i<exampleImageList.size(); ++i) {
+ if (exampleImageList[i].contains("doc/images")) {
+ QString t = exampleImageList[i].left(exampleImageList[i].lastIndexOf("doc/images")+10);
+ if (!exampleImageDirs.contains(t)) {
+ exampleImageDirs.insert(t);
+ }
}
}
- }
- Generator::augmentImageDirs(exampleImageDirs);
+ Generator::augmentImageDirs(exampleImageDirs);
+
+ /*
+ Parse each header file in the set using the appropriate parser and add it
+ to the big tree.
+ */
+ QSet<CodeParser *> usedParsers;
+
+ Generator::debug("Parsing header files");
+ int parsed = 0;
+ QMap<QString,QString>::ConstIterator h = headers.constBegin();
+ while (h != headers.constEnd()) {
+ CodeParser *codeParser = CodeParser::parserForHeaderFile(h.key());
+ if (codeParser) {
+ ++parsed;
+ Generator::debug(QString("Parsing " + h.key()));
+ codeParser->parseHeaderFile(config.location(), h.key());
+ usedParsers.insert(codeParser);
+ }
+ ++h;
+ }
- /*
- Parse each header file in the set using the appropriate parser and add it
- to the big tree.
- */
- QSet<CodeParser *> usedParsers;
-
- Generator::debug("Parsing header files");
- int parsed = 0;
- QMap<QString,QString>::ConstIterator h = headers.constBegin();
- while (h != headers.constEnd()) {
- CodeParser *codeParser = CodeParser::parserForHeaderFile(h.key());
- if (codeParser) {
- ++parsed;
- Generator::debug(QString("Parsing " + h.key()));
- codeParser->parseHeaderFile(config.location(), h.key());
- usedParsers.insert(codeParser);
+ foreach (CodeParser *codeParser, usedParsers)
+ codeParser->doneParsingHeaderFiles();
+
+ usedParsers.clear();
+ //qDebug() << "CALL: resolveInheritance()";
+ qdb->resolveInheritance();
+
+ /*
+ Parse each source text file in the set using the appropriate parser and
+ add it to the big tree.
+ */
+ parsed = 0;
+ Generator::debug("Parsing source files");
+ QMap<QString,QString>::ConstIterator s = sources.constBegin();
+ while (s != sources.constEnd()) {
+ CodeParser *codeParser = CodeParser::parserForSourceFile(s.key());
+ if (codeParser) {
+ ++parsed;
+ Generator::debug(QString("Parsing " + s.key()));
+ codeParser->parseSourceFile(config.location(), s.key());
+ usedParsers.insert(codeParser);
+ }
+ ++s;
}
- ++h;
+ Generator::debug(QString("Parsing done."));
+
+ foreach (CodeParser *codeParser, usedParsers)
+ codeParser->doneParsingSourceFiles();
+
+ /*
+ Now the primary tree has been built from all the header and
+ source files. Resolve all the class names, function names,
+ targets, URLs, links, and other stuff that needs resolving.
+ */
+ Generator::debug("Resolving stuff prior to generating docs");
+ //qDebug() << "CALL: resolveIssues()";
+ qdb->resolveIssues();
}
+ else {
+ Generator::debug("Reading excludedirs");
+ excludedDirsList = config.getCanonicalPathList(CONFIG_EXCLUDEDIRS);
+ foreach (const QString &excludeDir, excludedDirsList) {
+ QString p = QDir::fromNativeSeparators(excludeDir);
+ QDir tmp(p);
+ if (tmp.exists())
+ excludedDirs.insert(p);
+ }
- foreach (CodeParser *codeParser, usedParsers)
- codeParser->doneParsingHeaderFiles();
-
- usedParsers.clear();
- qdb->resolveInheritance();
+ Generator::debug("Reading excludefiles");
+ excludedFilesList = config.getCanonicalPathList(CONFIG_EXCLUDEFILES);
+ foreach (const QString& excludeFile, excludedFilesList) {
+ QString p = QDir::fromNativeSeparators(excludeFile);
+ excludedFiles.insert(p);
+ }
- /*
- Parse each source text file in the set using the appropriate parser and
- add it to the big tree.
- */
- parsed = 0;
- Generator::debug("Parsing source files");
- QMap<QString,QString>::ConstIterator s = sources.constBegin();
- while (s != sources.constEnd()) {
- CodeParser *codeParser = CodeParser::parserForSourceFile(s.key());
- if (codeParser) {
- ++parsed;
- Generator::debug(QString("Parsing " + s.key()));
- codeParser->parseSourceFile(config.location(), s.key());
- usedParsers.insert(codeParser);
+ Generator::debug("Adding doc/image dirs found in exampledirs to imagedirs");
+ QSet<QString> exampleImageDirs;
+ QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles);
+ for (int i=0; i<exampleImageList.size(); ++i) {
+ if (exampleImageList[i].contains("doc/images")) {
+ QString t = exampleImageList[i].left(exampleImageList[i].lastIndexOf("doc/images")+10);
+ if (!exampleImageDirs.contains(t)) {
+ exampleImageDirs.insert(t);
+ }
+ }
}
- ++s;
+ Generator::augmentImageDirs(exampleImageDirs);
+ qdb->resolveStuff();
}
- Generator::debug(QString("Parsing done."));
-
- foreach (CodeParser *codeParser, usedParsers)
- codeParser->doneParsingSourceFiles();
/*
- Now the big tree has been built from all the header and
- source files. Resolve all the class names, function names,
- targets, URLs, links, and other stuff that needs resolving.
- */
- Generator::debug("Resolving stuff prior to generating docs");
- qdb->resolveIssues();
-
- /*
- The tree is built and all the stuff that needed resolving
- has been resolved. Now traverse the tree and generate the
- documentation output. More than one output format can be
- requested. The tree is traversed for each one.
+ The primary tree is built and all the stuff that needed
+ resolving has been resolved. Now traverse the tree and
+ generate the documentation output. More than one output
+ format can be requested. The tree is traversed for each
+ one.
*/
Generator::debug("Generating docs");
+ //qDebug() << "CALL: generateDocs()";
QSet<QString>::ConstIterator of = outputFormats.constBegin();
while (of != outputFormats.constEnd()) {
Generator* generator = Generator::generatorForFormat(*of);
if (generator == 0)
- outputFormatsLocation.fatal(QCoreApplication::translate("QDoc", "Unknown output format '%1'").arg(*of));
+ outputFormatsLocation.fatal(QCoreApplication::translate("QDoc",
+ "Unknown output format '%1'").arg(*of));
generator->generateDocs();
++of;
}
-
//Generator::writeOutFileNames();
- Generator::debug("Shutting down qdoc");
+ Generator::debug("Terminating qdoc classes");
if (Generator::debugging())
Generator::stopDebugging(project);
@@ -492,17 +558,7 @@ static void processQdocconfFile(const QString &fileName)
Location::terminate();
QDir::setCurrent(prevCurrentDir);
-#ifndef QT_NO_TRANSLATION
- qDeleteAll(translators);
-#endif
-#ifdef DEBUG_SHUTDOWN_CRASH
- qDebug() << "main(): Delete qdoc database";
-#endif
- QDocDatabase::destroyQdocDB();
-#ifdef DEBUG_SHUTDOWN_CRASH
- qDebug() << "main(): qdoc database deleted";
-#endif
- Generator::debug("qdoc finished!");
+ Generator::debug("qdoc classes terminated");
}
extern Q_CORE_EXPORT QBasicAtomicInt qt_qhash_seed;
@@ -621,12 +677,17 @@ int main(int argc, char **argv)
logProgressOption.setDescription(QCoreApplication::translate("qdoc", "Log progress on stderr."));
parser.addOption(logProgressOption);
+ QCommandLineOption singleExecOption(QStringList() << QStringLiteral("single-exec"));
+ singleExecOption.setDescription(QCoreApplication::translate("qdoc", "Run qdoc once over all the qdoc conf files."));
+ parser.addOption(singleExecOption);
+
parser.process(app);
defines += parser.values(defineOption);
dependModules += parser.values(dependsOption);
highlighting = parser.isSet(highlightingOption);
showInternal = parser.isSet(showInternalOption);
+ singleExec = parser.isSet(singleExecOption);
redirectDocumentationToDevNull = parser.isSet(redirectDocumentationToDevNullOption);
Config::generateExamples = !parser.isSet(noExamplesOption);
foreach (const QString &indexDir, parser.values(indexDirOption)) {
@@ -650,21 +711,71 @@ int main(int argc, char **argv)
Generator::setQDocPass(Generator::Prepare);
if (parser.isSet(generateOption))
Generator::setQDocPass(Generator::Generate);
+ if (parser.isSet(singleExecOption))
+ Generator::setSingleExec();
if (parser.isSet(logProgressOption))
Location::startLoggingProgress();
- const QStringList qdocFiles = parser.positionalArguments();
+ /*
+ The default indent for code is 4.
+ The default value for false is 0.
+ The default supported file extensions are cpp, h, qdoc and qml.
+ The default language is c++.
+ The default output format is html.
+ The default tab size is 8.
+ And those are all the default values for configuration variables.
+ */
+ if (defaults.isEmpty()) {
+ defaults.insert(CONFIG_CODEINDENT, QLatin1String("4"));
+ defaults.insert(CONFIG_FALSEHOODS, QLatin1String("0"));
+ defaults.insert(CONFIG_FILEEXTENSIONS, QLatin1String("*.cpp *.h *.qdoc *.qml"));
+ defaults.insert(CONFIG_LANGUAGE, QLatin1String("Cpp"));
+ defaults.insert(CONFIG_OUTPUTFORMATS, QLatin1String("HTML"));
+ defaults.insert(CONFIG_TABSIZE, QLatin1String("8"));
+ }
+
+ QStringList qdocFiles = parser.positionalArguments();
if (qdocFiles.isEmpty())
parser.showHelp();
+ if (singleExec)
+ qdocFiles = Config::loadMaster(qdocFiles.at(0));
+
/*
- Main loop.
+ Main loop is now modified to handle single exec mode.
*/
+ if (Generator::singleExec())
+ Generator::setQDocPass(Generator::Prepare);
foreach (const QString &qf, qdocFiles) {
- //qDebug() << "PROCESSING:" << qf;
+ dependModules.clear();
processQdocconfFile(qf);
}
+ if (Generator::singleExec()) {
+ Generator::setQDocPass(Generator::Generate);
+ QDocDatabase* qdb = QDocDatabase::qdocDB();
+ qdb->processForest();
+ foreach (const QString &qf, qdocFiles) {
+ dependModules.clear();
+ processQdocconfFile(qf);
+ }
+ }
+
+#ifndef QT_NO_TRANSLATION
+ if (!translators.isEmpty()) {
+ for (int i=0; i<translators.size(); ++i) {
+ delete translators.at(i).second;
+ }
+ }
+ translators.clear();
+#endif
+
+#ifdef DEBUG_SHUTDOWN_CRASH
+ qDebug() << "main(): Delete qdoc database";
+#endif
+ QDocDatabase::destroyQdocDB();
+#ifdef DEBUG_SHUTDOWN_CRASH
+ qDebug() << "main(): qdoc database deleted";
+#endif
return EXIT_SUCCESS;
}
-
diff --git a/src/tools/qdoc/node.cpp b/src/tools/qdoc/node.cpp
index ce655efef6..f012aae87a 100644
--- a/src/tools/qdoc/node.cpp
+++ b/src/tools/qdoc/node.cpp
@@ -218,7 +218,7 @@ Node::Node(Type type, InnerNode *parent, const QString& name)
{
if (parent_)
parent_->addChild(this);
- outSubDir_ = CodeParser::currentOutputSubdirectory();
+ outSubDir_ = Generator::outputSubdir();
if (operators_.isEmpty()) {
operators_.insert("++","inc");
operators_.insert("--","dec");
diff --git a/src/tools/qdoc/qdocdatabase.cpp b/src/tools/qdoc/qdocdatabase.cpp
index fff78b1cbc..a22244d174 100644
--- a/src/tools/qdoc/qdocdatabase.cpp
+++ b/src/tools/qdoc/qdocdatabase.cpp
@@ -128,142 +128,54 @@ Tree* QDocForest::nextTree()
*/
/*!
+ Finds the tree for module \a t in the forest and
+ sets the primary tree to be that tree. After the
+ primary tree is set, that tree is removed from the
+ forest.
+
+ \node It gets re-inserted into the forest after the
+ search order is built.
+ */
+void QDocForest::setPrimaryTree(const QString& t)
+{
+ primaryTree_ = findTree(t);
+ forest_.remove(t);
+ if (!primaryTree_)
+ qDebug() << "ERROR: Could not set primary tree to:" << t;
+}
+
+/*!
If the search order array is empty, create the search order.
If the search order array is not empty, do nothing.
*/
-void QDocForest::setSearchOrder()
+void QDocForest::setSearchOrder(QStringList& t)
{
if (!searchOrder_.isEmpty())
return;
- QString primaryName = primaryTree()->moduleName();
- searchOrder_.clear();
+
+ /* Allocate space for the search order. */
searchOrder_.reserve(forest_.size()+1);
+ searchOrder_.clear();
moduleNames_.reserve(forest_.size()+1);
+ moduleNames_.clear();
+
+ /* The primary tree is always first in the search order. */
+ QString primaryName = primaryTree()->moduleName();
searchOrder_.append(primaryTree_);
moduleNames_.append(primaryName);
+ forest_.remove(primaryName);
+
QMap<QString, Tree*>::iterator i;
- if (primaryName != "QtCore") {
- i = forest_.find("QtCore");
- if (i != forest_.end()) {
- searchOrder_.append(i.value());
- moduleNames_.append("QtCore");
- forest_.erase(i);
- }
- }
- if (primaryName != "QtGui") {
- i = forest_.find("QtGui");
- if (i != forest_.end()) {
- searchOrder_.append(i.value());
- moduleNames_.append("QtGui");
- forest_.erase(i);
- }
- }
- if (primaryName != "QtNetwork") {
- i = forest_.find("QtNetwork");
- if (i != forest_.end()) {
- searchOrder_.append(i.value());
- moduleNames_.append("QtNetwork");
- forest_.erase(i);
- }
- }
- if (primaryName != "QtOpenGL") {
- i = forest_.find("QtOpenGL");
- if (i != forest_.end()) {
- searchOrder_.append(i.value());
- moduleNames_.append("QtOpenGL");
- forest_.erase(i);
- }
- }
- if (primaryName != "QtWidgets") {
- i = forest_.find("QtWidgets");
- if (i != forest_.end()) {
- searchOrder_.append(i.value());
- moduleNames_.append("QtWidgets");
- forest_.erase(i);
- }
- }
- if (primaryName != "QtSql") {
- i = forest_.find("QtSql");
- if (i != forest_.end()) {
- searchOrder_.append(i.value());
- moduleNames_.append("QtSql");
- forest_.erase(i);
- }
- }
- if (primaryName != "QtXml") {
- i = forest_.find("QtXml");
- if (i != forest_.end()) {
- searchOrder_.append(i.value());
- moduleNames_.append("QtXml");
- forest_.erase(i);
- }
- }
- if (primaryName != "QtSvg") {
- i = forest_.find("QtSvg");
- if (i != forest_.end()) {
- searchOrder_.append(i.value());
- moduleNames_.append("QtSvg");
- forest_.erase(i);
- }
- }
- if (primaryName != "QtDoc") {
- i = forest_.find("QtDoc");
- if (i != forest_.end()) {
- searchOrder_.append(i.value());
- moduleNames_.append("QtDoc");
- forest_.erase(i);
- }
- }
- if (primaryName != "QtQuick") {
- i = forest_.find("QtQuick");
- if (i != forest_.end()) {
- searchOrder_.append(i.value());
- moduleNames_.append("QtQuick");
- forest_.erase(i);
- }
- }
- if (primaryName != "QtQml") {
- i = forest_.find("QtQml");
- if (i != forest_.end()) {
- searchOrder_.append(i.value());
- moduleNames_.append("QtQml");
- forest_.erase(i);
- }
- }
- if (primaryName != "QtPrintSupport") {
- i = forest_.find("QtPrintSupport");
- if (i != forest_.end()) {
- searchOrder_.append(i.value());
- moduleNames_.append("QtPrintSupport");
- forest_.erase(i);
- }
- }
- if (primaryName != "QtGraphicalEffects") {
- i = forest_.find("QtGraphicalEffects");
- if (i != forest_.end()) {
- searchOrder_.append(i.value());
- moduleNames_.append("QtGraphicalEffects");
- forest_.erase(i);
- }
- }
- if (primaryName != "QtConcurrent") {
- i = forest_.find("QtConcurrent");
- if (i != forest_.end()) {
- searchOrder_.append(i.value());
- moduleNames_.append("QtConcurrent");
- forest_.erase(i);
- }
- }
-#if 0
- if (primaryName != "zzz") {
- i = forest_.find("zzz");
- if (i != forest_.end()) {
- searchOrder_.append(i.value());
- moduleNames_.append("zzz");
- forest_.erase(i);
+ foreach (QString m, t) {
+ if (primaryName != m) {
+ i = forest_.find(m);
+ if (i != forest_.end()) {
+ searchOrder_.append(i.value());
+ moduleNames_.append(m);
+ forest_.remove(m);
+ }
}
}
-#endif
/*
If any trees remain in the forest, just add them
to the search order sequentially, because we don't
@@ -283,15 +195,21 @@ void QDocForest::setSearchOrder()
Rebuild the forest after constructing the search order.
It was destroyed during construction of the search order,
but it is needed for module-specific searches.
+
+ Note that this loop also inserts the primary tree into the
+ forrest. That is a requirement.
*/
for (int i=0; i<searchOrder_.size(); ++i) {
- forest_.insert(moduleNames_.at(i).toLower(), searchOrder_.at(i));
+ if (!forest_.contains(moduleNames_.at(i))) {
+ forest_.insert(moduleNames_.at(i), searchOrder_.at(i));
+ }
}
-
#if 0
- qDebug() << " SEARCH ORDER:";
+ qDebug() << " SEARCH ORDER:";
for (int i=0; i<moduleNames_.size(); ++i)
- qDebug() << " " << i+1 << "." << moduleNames_.at(i);
+ qDebug() << " " << i+1 << "." << moduleNames_.at(i);
+ qDebug() << " FOREST:" << forest_.keys();
+ qDebug() << "SEARCH ORDER:" << moduleNames_;
#endif
}
@@ -356,6 +274,7 @@ 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();
@@ -363,10 +282,11 @@ NamespaceNode* QDocForest::newIndexTree(const QString& module)
/*!
Create a new Tree for use as the primary tree. This tree
- will represent the primary module.
+ will represent the primary module. \a module is camel case.
*/
void QDocForest::newPrimaryTree(const QString& module)
{
+ //qDebug() << " New primary tree:" << module;
primaryTree_ = new Tree(module, qdb_);
}
@@ -445,8 +365,19 @@ NodeMap QDocDatabase::typeNodeMap_;
constructs the \a forest_ object, which is also a singleton.
\a showInternal_ is normally false. If it is true, qdoc will
write documentation for nodes marked \c internal.
+
+ \a singleExec_ is false when qdoc is being used in the standard
+ way of running qdoc twices for each module, first with -prepare
+ and then with -generate. First the -prepare phase is run for
+ each module, then the -generate phase is run for each module.
+
+ When \a singleExec_ is true, qdoc is run only once. During the
+ single execution, qdoc processes the qdocconf files for all the
+ modules sequentially in a loop. Each source file for each module
+ is read exactly once.
*/
-QDocDatabase::QDocDatabase() : showInternal_(false), forest_(this)
+QDocDatabase::QDocDatabase()
+ : showInternal_(false), singleExec_(false), forest_(this)
{
// nothing
}
@@ -809,13 +740,41 @@ QmlClassNode* QDocDatabase::findQmlType(const ImportRec& import, const QString&
}
/*!
- This function calls \a func for each tree in the forest.
+ This function calls a set of functions for each tree in the
+ forest that has not already been analyzed. In this way, when
+ running qdoc in \e singleExec mode, each tree is analyzed in
+ turn, and its classes and types are added to the appropriate
+ node maps.
+ */
+void QDocDatabase::processForest()
+{
+ Tree* t = forest_.firstTree();
+ while (t) {
+ findAllNamespaces(t->root());
+ findAllClasses(t->root());
+ findAllFunctions(t->root());
+ findAllObsoleteThings(t->root());
+ findAllLegaleseTexts(t->root());
+ findAllSince(t->root());
+ t->setTreeHasBeenAnalyzed();
+ t = forest_.nextTree();
+ }
+}
+
+/*!
+ This function calls \a func for each tree in the forest,
+ but only if Tree::treeHasBeenAnalyzed() returns false for
+ the tree. In this way, when running qdoc in \e singleExec
+ mode, each tree is analyzed in turn, and its classes and
+ types are added to the appropriate node maps.
*/
void QDocDatabase::processForest(void (QDocDatabase::*func) (InnerNode*))
{
Tree* t = forest_.firstTree();
while (t) {
- (this->*(func))(t->root());
+ if (!t->treeHasBeenAnalyzed()) {
+ (this->*(func))(t->root());
+ }
t = forest_.nextTree();
}
}
@@ -887,7 +846,7 @@ NodeMap& QDocDatabase::getNamespaces()
*/
NodeMap& QDocDatabase::getServiceClasses()
{
- if (nonCompatClasses_.isEmpty() && qmlClasses_.isEmpty())
+ if (cppClasses_.isEmpty() && qmlTypes_.isEmpty())
processForest(&QDocDatabase::findAllClasses);
return serviceClasses_;
}
@@ -899,7 +858,7 @@ NodeMap& QDocDatabase::getServiceClasses()
*/
NodeMap& QDocDatabase::getQmlBasicTypes()
{
- if (nonCompatClasses_.isEmpty() && qmlBasicTypes_.isEmpty())
+ if (cppClasses_.isEmpty() && qmlBasicTypes_.isEmpty())
processForest(&QDocDatabase::findAllClasses);
return qmlBasicTypes_;
}
@@ -911,9 +870,9 @@ NodeMap& QDocDatabase::getQmlBasicTypes()
*/
NodeMap& QDocDatabase::getQmlTypes()
{
- if (nonCompatClasses_.isEmpty() && qmlClasses_.isEmpty())
+ if (cppClasses_.isEmpty() && qmlTypes_.isEmpty())
processForest(&QDocDatabase::findAllClasses);
- return qmlClasses_;
+ return qmlTypes_;
}
/*!
@@ -935,7 +894,7 @@ NodeMap& QDocDatabase::getObsoleteClasses()
*/
NodeMap& QDocDatabase::getCompatibilityClasses()
{
- if (nonCompatClasses_.isEmpty() && qmlClasses_.isEmpty())
+ if (cppClasses_.isEmpty() && qmlTypes_.isEmpty())
processForest(&QDocDatabase::findAllClasses);
return compatClasses_;
}
@@ -950,7 +909,7 @@ NodeMap& QDocDatabase::getCompatibilityClasses()
*/
NodeMap& QDocDatabase::getMainClasses()
{
- if (nonCompatClasses_.isEmpty() && qmlClasses_.isEmpty())
+ if (cppClasses_.isEmpty() && qmlTypes_.isEmpty())
processForest(&QDocDatabase::findAllClasses);
return mainClasses_;
}
@@ -962,9 +921,9 @@ NodeMap& QDocDatabase::getMainClasses()
*/
NodeMap& QDocDatabase::getCppClasses()
{
- if (nonCompatClasses_.isEmpty() && qmlClasses_.isEmpty())
+ if (cppClasses_.isEmpty() && qmlTypes_.isEmpty())
processForest(&QDocDatabase::findAllClasses);
- return nonCompatClasses_;
+ return cppClasses_;
}
/*!
@@ -987,7 +946,7 @@ void QDocDatabase::findAllClasses(InnerNode* node)
compatClasses_.insert(className, *c);
}
else {
- nonCompatClasses_.insert(className, *c);
+ cppClasses_.insert(className, *c);
if ((*c)->status() == Node::Main)
mainClasses_.insert(className, *c);
}
@@ -1000,9 +959,9 @@ void QDocDatabase::findAllClasses(InnerNode* node)
else if (((*c)->isQmlType() || (*c)->isQmlBasicType())&& !(*c)->doc().isEmpty()) {
QString qmlTypeName = (*c)->name();
if (qmlTypeName.startsWith(QLatin1String("QML:")))
- qmlClasses_.insert(qmlTypeName.mid(4),*c);
+ qmlTypes_.insert(qmlTypeName.mid(4),*c);
else
- qmlClasses_.insert(qmlTypeName,*c);
+ qmlTypes_.insert(qmlTypeName,*c);
//also add to the QML basic type map
if ((*c)->isQmlBasicType())
@@ -1022,7 +981,6 @@ void QDocDatabase::findAllClasses(InnerNode* node)
*/
NodeMapMap& QDocDatabase::getFunctionIndex()
{
- funcIndex_.clear();
processForest(&QDocDatabase::findAllFunctions);
return funcIndex_;
}
@@ -1314,7 +1272,15 @@ const NodeMultiMap& QDocDatabase::getSinceMap(const QString& key)
*/
void QDocDatabase::resolveIssues() {
resolveQmlInheritance(primaryTreeRoot());
- resolveTargets();
+ primaryTree()->resolveTargets(primaryTreeRoot());
+ primaryTree()->resolveCppToQmlLinks();
+}
+
+void QDocDatabase::resolveStuff()
+{
+ primaryTree()->resolveInheritance();
+ resolveQmlInheritance(primaryTreeRoot());
+ //primaryTree()->resolveTargets(primaryTreeRoot());
primaryTree()->resolveCppToQmlLinks();
}
@@ -1424,10 +1390,18 @@ void QDocDatabase::generateTagFile(const QString& name, Generator* g)
}
/*!
- Reads and parses the qdoc index files listed in \a indexFiles.
+ Reads and parses the qdoc index files listed in \a t.
*/
-void QDocDatabase::readIndexes(const QStringList& indexFiles)
+void QDocDatabase::readIndexes(const QStringList& t)
{
+ QStringList indexFiles;
+ foreach (const QString& f, t) {
+ QString fn = f.mid(f.lastIndexOf(QChar('/'))+1);
+ if (!isLoaded(fn))
+ indexFiles << f;
+ else
+ qDebug() << "This index file is already in memory:" << f;
+ }
QDocIndexFiles::qdocIndexFiles()->readIndexes(indexFiles);
QDocIndexFiles::destroyQDocIndexFiles();
}
@@ -1443,6 +1417,8 @@ void QDocDatabase::generateIndex(const QString& fileName,
Generator* g,
bool generateInternalNodes)
{
+ QString t = fileName.mid(fileName.lastIndexOf(QChar('/'))+1);
+ primaryTree()->setIndexFileName(t);
QDocIndexFiles::qdocIndexFiles()->generateIndex(fileName, url, title, g, generateInternalNodes);
QDocIndexFiles::destroyQDocIndexFiles();
}
diff --git a/src/tools/qdoc/qdocdatabase.h b/src/tools/qdoc/qdocdatabase.h
index 8b67aca971..f6deeeece8 100644
--- a/src/tools/qdoc/qdocdatabase.h
+++ b/src/tools/qdoc/qdocdatabase.h
@@ -78,7 +78,14 @@ class QDocForest
bool done() { return (currentIndex_ >= searchOrder().size()); }
const QVector<Tree*>& searchOrder();
const QVector<Tree*>& indexSearchOrder();
- void setSearchOrder();
+ void setSearchOrder(QStringList& t);
+ bool isLoaded(const QString& fn) {
+ foreach (Tree* t, searchOrder()) {
+ if (fn == t->indexFileName())
+ return true;
+ }
+ return false;
+ }
const Node* findNode(const QStringList& path,
const Node* relative,
@@ -186,8 +193,11 @@ class QDocForest
}
}
+ void clearSearchOrder() { searchOrder_.clear(); }
+
private:
void newPrimaryTree(const QString& module);
+ void setPrimaryTree(const QString& t);
NamespaceNode* newIndexTree(const QString& module);
private:
@@ -274,12 +284,10 @@ class QDocDatabase
void resolveInheritance() { primaryTree()->resolveInheritance(); }
void resolveQmlInheritance(InnerNode* root);
void resolveIssues();
+ void resolveStuff();
void fixInheritance() { primaryTree()->fixInheritance(); }
void resolveProperties() { primaryTree()->resolveProperties(); }
- void resolveTargets() {
- primaryTree()->resolveTargets(primaryTreeRoot());
- }
void insertTarget(const QString& name,
const QString& title,
TargetRec::Type type,
@@ -355,18 +363,22 @@ class QDocDatabase
void clearOpenNamespaces() { openNamespaces_.clear(); }
void insertOpenNamespace(const QString& path) { openNamespaces_.insert(path); }
void setShowInternal(bool value) { showInternal_ = value; }
+ void setSingleExec(bool value) { singleExec_ = value; }
+ void processForest();
// Try to make this function private.
QDocForest& forest() { return forest_; }
NamespaceNode* primaryTreeRoot() { return forest_.primaryTreeRoot(); }
void newPrimaryTree(const QString& module) { forest_.newPrimaryTree(module); }
+ void setPrimaryTree(const QString& t) { forest_.setPrimaryTree(t); }
NamespaceNode* newIndexTree(const QString& module) { return forest_.newIndexTree(module); }
const QVector<Tree*>& searchOrder() { return forest_.searchOrder(); }
void setLocalSearch() { forest_.searchOrder_ = QVector<Tree*>(1, primaryTree()); }
void setSearchOrder(const QVector<Tree*>& searchOrder) { forest_.searchOrder_ = searchOrder; }
- void setSearchOrder() { forest_.setSearchOrder(); }
+ void setSearchOrder(QStringList& t) { forest_.setSearchOrder(t); }
void mergeCollections(Node::Type nt, CNMap& cnm, const Node* relative);
void mergeCollections(CollectionNode* cn);
+ void clearSearchOrder() { forest_.clearSearchOrder(); }
private:
friend class QDocIndexFiles;
@@ -379,6 +391,7 @@ class QDocDatabase
return forest_.findNode(path, relative, findFlags, genus);
}
void processForest(void (QDocDatabase::*) (InnerNode*));
+ bool isLoaded(const QString& t) { return forest_.isLoaded(t); }
static void initializeDB();
private:
@@ -394,20 +407,21 @@ class QDocDatabase
static QDocDatabase* qdocDB_;
static NodeMap typeNodeMap_;
bool showInternal_;
+ bool singleExec_;
QString version_;
QDocForest forest_;
- NodeMap nonCompatClasses_;
- NodeMap mainClasses_;
+ NodeMap cppClasses_;
+ NodeMap mainClasses_; // MWS: not needed, should be delete
NodeMap compatClasses_;
NodeMap obsoleteClasses_;
NodeMap classesWithObsoleteMembers_;
NodeMap obsoleteQmlTypes_;
NodeMap qmlTypesWithObsoleteMembers_;
NodeMap namespaceIndex_;
- NodeMap serviceClasses_;
+ NodeMap serviceClasses_; // MWS: not needed, should be deleted
NodeMap qmlBasicTypes_;
- NodeMap qmlClasses_;
+ NodeMap qmlTypes_;
NodeMapMap newClassMaps_;
NodeMapMap newQmlTypeMaps_;
NodeMultiMapMap newSinceMaps_;
diff --git a/src/tools/qdoc/qdocindexfiles.cpp b/src/tools/qdoc/qdocindexfiles.cpp
index 7445292a56..1e49dd08a0 100644
--- a/src/tools/qdoc/qdocindexfiles.cpp
+++ b/src/tools/qdoc/qdocindexfiles.cpp
@@ -564,7 +564,6 @@ void QDocIndexFiles::readIndexSection(const QDomElement& element,
node->setReconstitutedBrief(briefAttr);
}
- // zzz
bool useParent = (element.nodeName() == "namespace" && name.isEmpty());
if (element.hasChildNodes()) {
QDomElement child = element.firstChildElement();
@@ -806,11 +805,14 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer,
QString fullName = node->fullDocumentName();
if (fullName != objName)
writer.writeAttribute("fullname", fullName);
+#if 0
if (Generator::useOutputSubdirs())
href = node->outputSubdirectory();
if (!href.isEmpty())
href.append(QLatin1Char('/'));
href.append(gen_->fullDocumentLocation(node));
+#endif
+ href = gen_->fullDocumentLocation(node);
}
else
href = node->name();
diff --git a/src/tools/qdoc/tokenizer.cpp b/src/tools/qdoc/tokenizer.cpp
index 7c9e9f338a..dca1e9bb55 100644
--- a/src/tools/qdoc/tokenizer.cpp
+++ b/src/tools/qdoc/tokenizer.cpp
@@ -511,6 +511,9 @@ void Tokenizer::initialize(const Config &config)
defines = new QRegExp(d.join('|'));
falsehoods = new QRegExp(config.getStringList(CONFIG_FALSEHOODS).join('|'));
+ /*
+ The keyword hash table is always cleared before any words are inserted.
+ */
memset(kwordHashTable, 0, sizeof(kwordHashTable));
for (int i = 0; i < Tok_LastKeyword - Tok_FirstKeyword + 1; i++)
insertKwordIntoHash(kwords[i], i + 1);
@@ -533,6 +536,11 @@ void Tokenizer::initialize(const Config &config)
}
}
+/*!
+ The heap allocated variables are freed here. The keyword
+ hash table is not cleared here, but it is cleared in the
+ initialize() function, before any keywords are inserted.
+ */
void Tokenizer::terminate()
{
delete comment;
diff --git a/src/tools/qdoc/tree.cpp b/src/tools/qdoc/tree.cpp
index 2e327a5ac8..d4169f6242 100644
--- a/src/tools/qdoc/tree.cpp
+++ b/src/tools/qdoc/tree.cpp
@@ -66,7 +66,11 @@ QT_BEGIN_NAMESPACE
be necessary, and it might be removed later.
*/
Tree::Tree(const QString& module, QDocDatabase* qdb)
- : module_(module), qdb_(qdb), root_(0, QString())
+ : treeHasBeenAnalyzed_(false),
+ docsHaveBeenGenerated_(false),
+ module_(module),
+ qdb_(qdb),
+ root_(0, QString())
{
root_.setModuleName(module_);
root_.setTree(this);
diff --git a/src/tools/qdoc/tree.h b/src/tools/qdoc/tree.h
index 6bb13ee327..5f11a81405 100644
--- a/src/tools/qdoc/tree.h
+++ b/src/tools/qdoc/tree.h
@@ -187,12 +187,22 @@ class Tree
void addExampleNode(ExampleNode* n) { exampleNodeMap_.insert(n->title(), n); }
ExampleNodeMap& exampleNodeMap() { return exampleNodeMap_; }
const Node* checkForCollision(const QString& name);
+ void setIndexFileName(const QString& t) { indexFileName_ = t; }
+
+ bool treeHasBeenAnalyzed() const { return treeHasBeenAnalyzed_; }
+ bool docsHaveBeenGenerated() const { return docsHaveBeenGenerated_; }
+ void setTreeHasBeenAnalyzed() { treeHasBeenAnalyzed_ = true; }
+ void setdocsHaveBeenGenerated() { docsHaveBeenGenerated_ = true; }
public:
const QString& moduleName() const { return module_; }
+ const QString& indexFileName() const { return indexFileName_; }
private:
+ bool treeHasBeenAnalyzed_;
+ bool docsHaveBeenGenerated_;
QString module_;
+ QString indexFileName_;
QDocDatabase* qdb_;
NamespaceNode root_;
PropertyMap unresolvedPropertyMap;