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::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& commonMetaCommands(); @@ -89,9 +88,9 @@ protected: QDocDatabase* qdb_; private: - static QString currentSubDir_; static QList 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"); @@ -868,6 +873,36 @@ bool Config::isMetaKeyChar(QChar ch) || ch == QLatin1Char(','); } +/*! + \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 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 &excludedDirs, const QSet &excludedFiles); QStringList getExampleImageFiles(const QSet &excludedDirs, const QSet &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& metaCommandSet) -{ - priv = new DocPrivate(start_loc,end_loc,source); - DocParser parser; - parser.parse(source,priv,metaCommandSet,QSet()); -} -#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.

\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() << ""; } @@ -2837,7 +2850,7 @@ void HtmlGenerator::generateFunctionIndex(const Node *relative) char currentLetter; out() << "
    \n"; - NodeMapMap funcIndex = qdb_->getFunctionIndex(); + NodeMapMap& funcIndex = qdb_->getFunctionIndex(); QMap::ConstIterator f = funcIndex.constBegin(); while (f != funcIndex.constEnd()) { out() << "
  • "; @@ -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 defaults; +#ifndef QT_NO_TRANSLATION +typedef QPair Translator; +static QList 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,42 +202,25 @@ static void loadIndexFiles(Config& config) */ static void processQdocconfFile(const QString &fileName) { -#ifndef QT_NO_TRANSLATION - QList 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. + + 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. */ 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. - */ - static QHash 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")); - } - QHash::iterator iter; for (iter = defaults.begin(); iter != defaults.end(); ++iter) config.setStringList(iter.key(), QStringList() << iter.value()); 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; iload(*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 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 excludedDirs; QSet 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 excludefiles"); - excludedFilesList = config.getCanonicalPathList(CONFIG_EXCLUDEFILES); - foreach (const QString& excludeFile, excludedFilesList) { - QString p = QDir::fromNativeSeparators(excludeFile); - excludedFiles.insert(p); - } + 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 headerdirs"); - headerList = config.getAllFiles(CONFIG_HEADERS,CONFIG_HEADERDIRS,excludedDirs,excludedFiles); - QMap headers; - QMultiMap headerFileNames; - for (int i=0; i sources; - QMultiMap sourceFileNames; - for (int i=0; i headers; + QMultiMap headerFileNames; + for (int i=0; i sources; + QMultiMap sourceFileNames; + for (int i=0; i exampleImageDirs; - QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles); - for (int i=0; i usedParsers; - - Generator::debug("Parsing header files"); - int parsed = 0; - QMap::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); + Generator::debug("Adding doc/image dirs found in exampledirs to imagedirs"); + QSet exampleImageDirs; + QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles); + for (int i=0; idoneParsingHeaderFiles(); + /* + Parse each header file in the set using the appropriate parser and add it + to the big tree. + */ + QSet usedParsers; - usedParsers.clear(); - 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::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("Parsing header files"); + int parsed = 0; + QMap::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; } - ++s; + + 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::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; + } + 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(); } - Generator::debug(QString("Parsing done.")); + 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->doneParsingSourceFiles(); + Generator::debug("Reading excludefiles"); + excludedFilesList = config.getCanonicalPathList(CONFIG_EXCLUDEFILES); + foreach (const QString& excludeFile, excludedFilesList) { + QString p = QDir::fromNativeSeparators(excludeFile); + excludedFiles.insert(p); + } + + Generator::debug("Adding doc/image dirs found in exampledirs to imagedirs"); + QSet exampleImageDirs; + QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles); + for (int i=0; iresolveStuff(); + } /* - 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::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; iaddChild(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 @@ -127,143 +127,55 @@ Tree* QDocForest::nextTree() Returns the pointer to the primary tree. */ +/*! + 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::iterator i; - if (primaryName != "QtCore") { - i = forest_.find("QtCore"); - if (i != forest_.end()) { - searchOrder_.append(i.value()); - moduleNames_.append("QtCore"); - 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); + } } } - 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); - } - } -#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& 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& searchOrder(); const QVector& 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& searchOrder() { return forest_.searchOrder(); } void setLocalSearch() { forest_.searchOrder_ = QVector(1, primaryTree()); } void setSearchOrder(const QVector& 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;