Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
5#include "codemarker.h"
6#include "codeparser.h"
7#include "config.h"
9#include "doc.h"
11#include "htmlgenerator.h"
13#include "location.h"
14#include "puredocparser.h"
15#include "qdocdatabase.h"
16#include "qmlcodemarker.h"
17#include "qmlcodeparser.h"
19#include "utilities.h"
20#ifdef QDOC_TEMPLATE_GENERATOR_ENABLED
21#include "templategenerator.h"
22#endif
23#include "tokenizer.h"
24#include "tree.h"
26
27#include "filesystem/fileresolver.h"
28#include "boundaries/filesystem/directorypath.h"
30
31#include <QtCore/qcompilerdetection.h>
32#include <QtCore/qdatetime.h>
33#include <QtCore/qdebug.h>
34#include <QtCore/qglobal.h>
35#include <QtCore/qhashfunctions.h>
36
37#include <set>
38
39#ifndef QT_BOOTSTRAPPED
40# include <QtCore/qcoreapplication.h>
41#endif
42
43#include <algorithm>
44#include <cstdlib>
45
47
48using namespace Qt::StringLiterals;
49
50bool creationTimeBefore(const QFileInfo &fi1, const QFileInfo &fi2)
51{
52 return fi1.lastModified() < fi2.lastModified();
53}
54
55/*!
56 \internal
57 Inspects each file path in \a sources. File paths with a known
58 source file type are parsed to extract user-provided
59 documentation and information about source code level elements.
60
61 \note Unknown source file types are silently ignored.
62
63 The validity or availability of the file paths may or may not cause QDoc
64 to generate warnings; this depends on the implementation of
65 parseSourceFile() for the relevant parser.
66
67 \sa CodeParser::parserForSourceFile, CodeParser::sourceFileNameFilter
68*/
69static void parseSourceFiles(
70 std::vector<QString>&& sources,
71 SourceFileParser& source_file_parser,
72 CppCodeParser& cpp_code_parser
73) {
74 ParserErrorHandler error_handler{};
75 std::stable_sort(sources.begin(), sources.end());
76
77 sources.erase (
78 std::unique(sources.begin(), sources.end()),
79 sources.end()
80 );
81
82 sources.erase(std::remove_if(sources.begin(), sources.end(),
83 [](const QString &source) {
84 return Utilities::isGeneratedFile(source);
85 }),
86 sources.end());
87
88 auto qml_sources =
89 std::stable_partition(sources.begin(), sources.end(), [](const QString& source){
90 return CodeParser::parserForSourceFile(source) == CodeParser::parserForLanguage("QML");
91 });
92
93
94 std::for_each(qml_sources, sources.end(),
95 [&source_file_parser, &cpp_code_parser, &error_handler](const QString& source){
96 qCDebug(lcQdoc, "Parsing %s", qPrintable(source));
97
98 auto [untied_documentation, tied_documentation] = source_file_parser(tag_source_file(source));
99 std::vector<FnMatchError> errors{};
100
101 for (const auto &untied : std::as_const(untied_documentation)) {
102 auto result = cpp_code_parser.processTopicArgs(untied);
103 tied_documentation.insert(tied_documentation.end(), result.first.begin(), result.first.end());
104 errors.insert(errors.end(), result.second.begin(), result.second.end());
105 };
106
107 cpp_code_parser.processMetaCommands(tied_documentation);
108
109 // Process errors that occurred during parsing
110 for (const auto &e : errors)
111 error_handler(e);
112 });
113
114 std::for_each(sources.begin(), qml_sources, [&cpp_code_parser](const QString& source){
115 auto *codeParser = CodeParser::parserForSourceFile(source);
116 if (!codeParser) return;
117
118 qCDebug(lcQdoc, "Parsing %s", qPrintable(source));
119 codeParser->parseSourceFile(Config::instance().location(), source, cpp_code_parser);
120 });
121
122}
123
124/*!
125 Read some XML indexes containing definitions from other
126 documentation sets. \a config contains a variable that
127 lists directories where index files can be found. It also
128 contains the \c depends variable, which lists the modules
129 that the current module depends on. \a formats contains
130 a list of output formats; each format may have a different
131 output subdirectory where index files are located.
132*/
133static void loadIndexFiles(const QSet<QString> &formats)
134{
135 Config &config = Config::instance();
137 QStringList indexFiles;
138 const QStringList configIndexes{config.get(CONFIG_INDEXES).asStringList()};
139 bool warn = !config.get(CONFIG_NOLINKERRORS).asBool();
140
141 for (const auto &index : configIndexes) {
142 QFileInfo fi(index);
143 if (fi.exists() && fi.isFile())
144 indexFiles << index;
145 else if (warn)
146 Location().warning(QString("Index file not found: %1").arg(index));
147 }
148
149 config.dependModules() += config.get(CONFIG_DEPENDS).asStringList();
150 config.dependModules().removeDuplicates();
151 bool useNoSubDirs = false;
152 QSet<QString> subDirs;
153
154 // Add format-specific output subdirectories to the set of
155 // subdirectories where we look for index files
156 for (const auto &format : formats) {
157 if (config.get(format + Config::dot + "nosubdirs").asBool()) {
158 useNoSubDirs = true;
159 const auto singleOutputSubdir{QDir(config.getOutputDir(format)).dirName()};
160 if (!singleOutputSubdir.isEmpty())
161 subDirs << singleOutputSubdir;
162 }
163 }
164
165 if (!config.dependModules().empty()) {
166 if (!config.indexDirs().empty()) {
167 for (auto &dir : config.indexDirs()) {
168 if (dir.startsWith("..")) {
169 const QString prefix(QDir(config.currentDir())
170 .relativeFilePath(config.previousCurrentDir()));
171 if (!prefix.isEmpty())
172 dir.prepend(prefix + QLatin1Char('/'));
173 }
174 }
175 /*
176 Load all dependencies:
177 Either add all subdirectories of the indexdirs as dependModules,
178 when an asterisk is used in the 'depends' list, or
179 when <format>.nosubdirs is set, we need to look for all .index files
180 in the output subdirectory instead.
181 */
182 bool asteriskUsed = false;
183 if (config.dependModules().contains("*")) {
184 config.dependModules().removeOne("*");
185 asteriskUsed = true;
186 if (useNoSubDirs) {
187 std::for_each(formats.begin(), formats.end(), [&](const QString &format) {
188 QDir scanDir(config.getOutputDir(format));
189 QStringList foundModules =
190 scanDir.entryList(QStringList("*.index"), QDir::Files);
191 std::transform(
192 foundModules.begin(), foundModules.end(), foundModules.begin(),
193 [](const QString &index) { return QFileInfo(index).baseName(); });
194 config.dependModules() << foundModules;
195 });
196 } else {
197 for (const auto &indexDir : config.indexDirs()) {
198 QDir scanDir = QDir(indexDir);
199 scanDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
200 QFileInfoList dirList = scanDir.entryInfoList();
201 for (const auto &dir : dirList)
202 config.dependModules().append(dir.fileName());
203 }
204 }
205 // Remove self-dependencies and possible duplicates
206 QString project{config.get(CONFIG_PROJECT).asString()};
207 config.dependModules().removeAll(project.toLower());
208 config.dependModules().removeDuplicates();
209 qCCritical(lcQdoc) << "Configuration file for"
210 << project << "has depends = *; loading all"
211 << config.dependModules().size()
212 << "index files found";
213 }
214 for (const auto &module : config.dependModules()) {
215 QList<QFileInfo> foundIndices;
216 // Always look in module-specific subdir, even with *.nosubdirs config
217 bool useModuleSubDir = !subDirs.contains(module);
218 subDirs << module;
219
220 for (const auto &dir : config.indexDirs()) {
221 for (const auto &subDir : std::as_const(subDirs)) {
222 QString fileToLookFor = dir + QLatin1Char('/') + subDir + QLatin1Char('/')
223 + module.toLower() + ".index";
224 if (QFile::exists(fileToLookFor)) {
225 QFileInfo tempFileInfo(fileToLookFor);
226 if (!foundIndices.contains(tempFileInfo))
227 foundIndices.append(tempFileInfo);
228 }
229 }
230 }
231 // Clear the temporary module-specific subdir
232 if (useModuleSubDir)
233 subDirs.remove(module);
234 std::sort(foundIndices.begin(), foundIndices.end(), creationTimeBefore);
235 QString indexToAdd;
236 if (foundIndices.size() > 1) {
237 /*
238 QDoc should always use the last entry in the multimap when there are
239 multiple index files for a module, since the last modified file has the
240 highest UNIX timestamp.
241 */
242 QStringList indexPaths;
243 indexPaths.reserve(foundIndices.size());
244 for (const auto &found : std::as_const(foundIndices))
245 indexPaths << found.absoluteFilePath();
246 if (warn) {
247 Location().warning(
248 QString("Multiple index files found for dependency \"%1\":\n%2")
249 .arg(module, indexPaths.join('\n')));
250 Location().warning(
251 QString("Using %1 as index file for dependency \"%2\"")
252 .arg(foundIndices[foundIndices.size() - 1].absoluteFilePath(),
253 module));
254 }
255 indexToAdd = foundIndices[foundIndices.size() - 1].absoluteFilePath();
256 } else if (foundIndices.size() == 1) {
257 indexToAdd = foundIndices[0].absoluteFilePath();
258 }
259 if (!indexToAdd.isEmpty()) {
260 if (!indexFiles.contains(indexToAdd))
261 indexFiles << indexToAdd;
262 } else if (!asteriskUsed && warn) {
263 Location().warning(
264 QString(R"("%1" Cannot locate index file for dependency "%2")")
265 .arg(config.get(CONFIG_PROJECT).asString(), module));
266 }
267 }
268 } else if (warn) {
269 Location().warning(
270 QLatin1String("Dependent modules specified, but no index directories were set. "
271 "There will probably be errors for missing links."));
272 }
273 }
274 qdb->readIndexes(indexFiles);
275}
276
277/*!
278 \internal
279 Prints to stderr the name of the project that QDoc is running for,
280 in which mode and which phase.
281
282 If QDoc is not running in debug mode or --log-progress command line
283 option is not set, do nothing.
284 */
285void logStartEndMessage(const QLatin1String &startStop, Config &config)
286{
287 if (!config.get(CONFIG_LOGPROGRESS).asBool())
288 return;
289
290 const QString runName = " qdoc for "
291 + config.get(CONFIG_PROJECT).asString()
292 + QLatin1String(" in ")
293 + QLatin1String(config.singleExec() ? "single" : "dual")
294 + QLatin1String(" process mode: ")
295 + QLatin1String(config.preparing() ? "prepare" : "generate")
296 + QLatin1String(" phase.");
297
298 const QString msg = startStop + runName;
299 qCInfo(lcQdoc) << msg.toUtf8().data();
300}
301
302/*!
303 \internal
304 Generates the index file for the current project.
305
306 Index files contain documentation metadata used for cross-module linking.
307 Index hrefs are relative paths to HTML files, so the index file must be
308 co-located with HTML output for correct link resolution.
309
310 Index generation only occurs during the prepare phase (single-exec mode)
311 or when not in generate-only mode (dual-exec mode).
312 */
313static void generateIndexFile(const Config &config)
314{
315 if (config.generating())
316 return;
317
318 const QString project = config.get(CONFIG_PROJECT).asString();
319 const QString url = config.get(CONFIG_URL).asString();
320 const QString description = config.get(CONFIG_DESCRIPTION)
321 .asString(project + " Reference Documentation"_L1);
322
323 // Index files must be co-located with HTML files for correct href resolution.
324 // Use HTML output directory when available; this handles HTML.outputsubdir.
325 QString outputDirPath;
326 if (config.getOutputFormats().contains(u"HTML"_s)) {
327 outputDirPath = config.getOutputDir(u"HTML"_s);
328 } else {
329 // Fallback for non-HTML builds (e.g., DocBook-only)
330 outputDirPath = Config::overrideOutputDir.isNull()
331 ? config.get(CONFIG_OUTPUTDIR).asString()
333 if (config.singleExec())
334 outputDirPath += '/'_L1 + project.toLower();
335 }
336
337 const QString fileBase = project.toLower().simplified().replace(' '_L1, '-'_L1);
338
339 OutputDirectory outputDir = OutputDirectory::ensure(outputDirPath, Location());
340 const QString indexFileName = fileBase + ".index"_L1;
341 const QString indexPath = outputDir.absoluteFilePath(indexFileName);
342
343 // Index files use HTML-style hrefs (e.g., "qstring.html#append"),
344 // so we pass the HTML generator for correct file extension handling.
345 auto *htmlGenerator = Generator::generatorForFormat(u"HTML"_s);
346
347 QDocDatabase::qdocDB()->generateIndex(indexPath, url, description, htmlGenerator);
348}
349
350/*!
351 Processes the qdoc config file \a fileName. This is the controller for all
352 of QDoc. The \a config instance represents the configuration data for QDoc.
353 All other classes are initialized with the same config.
354 */
355static void processQdocconfFile(const QString &fileName)
356{
357 Config &config = Config::instance();
358 config.setPreviousCurrentDir(QDir::currentPath());
359
360 /*
361 With the default configuration values in place, load
362 the qdoc configuration file. Note that the configuration
363 file may include other configuration files.
364
365 The Location class keeps track of the current location
366 in the file being processed, mainly for error reporting
367 purposes.
368 */
370 config.load(fileName);
371 QString project{config.get(CONFIG_PROJECT).asString()};
372 if (project.isEmpty()) {
373 qCCritical(lcQdoc) << QLatin1String("qdoc can't run; no project set in qdocconf file");
374 exit(1);
375 }
377
378 config.setCurrentDir(QFileInfo(fileName).path());
379 if (!config.currentDir().isEmpty())
380 QDir::setCurrent(config.currentDir());
381
382 logStartEndMessage(QLatin1String("Start"), config);
383
384 if (config.getDebug()) {
385 Utilities::startDebugging(QString("command line"));
386 qCDebug(lcQdoc).noquote() << "Arguments:" << QCoreApplication::arguments();
387 }
388
389 // <<TODO: [cleanup-temporary-kludges]
390 // The underlying validation should be performed at the
391 // configuration level during parsing.
392 // This cannot be done straightforwardly with how the Config class
393 // is implemented.
394 // When the Config class will be deprived of logic and
395 // restructured, the compiler will notify us of this kludge, but
396 // remember to reevaluate the code itself considering the new
397 // data-flow and the possibility for optimizations as this is not
398 // done for temporary code. Indeed some of the code is visibly wasteful.
399 // Similarly, ensure that the loose definition that we use here is
400 // not preserved.
401
402 QStringList search_directories{config.getCanonicalPathList(CONFIG_EXAMPLEDIRS)};
403 QStringList image_search_directories{config.getCanonicalPathList(CONFIG_IMAGEDIRS)};
404
405 const auto& [excludedDirs, excludedFiles] = config.getExcludedPaths();
406
407 qCDebug(lcQdoc, "Adding doc/image dirs found in exampledirs to imagedirs");
408 QSet<QString> exampleImageDirs;
409 QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles);
410 for (const auto &image : exampleImageList) {
411 if (image.contains("doc/images")) {
412 QString t = image.left(image.lastIndexOf("doc/images") + 10);
413 if (!exampleImageDirs.contains(t))
414 exampleImageDirs.insert(t);
415 }
416 }
417
418 // REMARK: The previous system discerned between search directories based on the kind of file that was searched for.
419 // For example, an image search was bounded to some directories
420 // that may or may not be the same as the ones where examples are
421 // searched for.
422 // The current Qt documentation does not use this feature. That
423 // is, the output of QDoc when a unified search list is used is
424 // the same as the output for that of separated lists.
425 // For this reason, we currently simplify the process, albeit this
426 // may at some point change, by joining the various lists into a
427 // single search list and a unified interface.
428 // Do note that the configuration still allows for those
429 // parameters to be user defined in a split-way as this will not
430 // be able to be changed until Config itself is looked upon.
431 // Hence, we join the various directory sources into one list for the time being.
432 // Do note that this means that the amount of searched directories for a file is now increased.
433 // This shouldn't matter as the amount of directories is expected
434 // to be generally small and the search routine complexity is
435 // linear in the amount of directories.
436 // There are some complications that may arise in very specific
437 // cases by this choice (some of which where there before under
438 // possibly different circumstances), making some files
439 // unreachable.
440 // See the remarks in FileResolver for more infomration.
441 std::copy(image_search_directories.begin(), image_search_directories.end(), std::back_inserter(search_directories));
442 std::copy(exampleImageDirs.begin(), exampleImageDirs.end(), std::back_inserter(search_directories));
443
444 std::vector<DirectoryPath> validated_search_directories{};
445 for (const QString& path : search_directories) {
446 auto maybe_validated_path{DirectoryPath::refine(path)};
447 if (!maybe_validated_path)
448 // TODO: [uncentralized-admonition]
449 qCDebug(lcQdoc).noquote() << u"%1 is not a valid path, it will be ignored when resolving a file"_s.arg(path);
450 else validated_search_directories.push_back(*maybe_validated_path);
451 }
452
453 // TODO>>
454
455 FileResolver file_resolver{std::move(validated_search_directories)};
456
457 // REMARK: The constructor for generators doesn't actually perform
458 // initialization of their content.
459 // Indeed, Generators use the general antipattern of the static
460 // initialize-terminate non-scoped mutable state that we see in
461 // many parts of QDoc.
462 // In their constructor, Generators mainly register themselves into a static list.
463 // Previously, this was done at the start of main.
464 // To be able to pass a correct FileResolver or other systems, we
465 // need to construct them after the configuration has been read
466 // and has been destructured.
467 // For this reason, their construction was moved here.
468 // This function may be called more than once for some of QDoc's
469 // call, albeit this should not actually happen in Qt's
470 // documentation.
471 // Then, constructing the generators here might provide for some
472 // unexpected behavior as new generators are appended to the list
473 // and never used, considering that the list is searched in a
474 // linearly fashion and each generator of some type T, in the
475 // current codebase, will always be found if another instance of
476 // that same type would have been found.
477 // Furthermore, those instances would be destroyed by then, such
478 // that accessing them would be erroneous.
479 // To avoid this, the static list was made to be cleared in
480 // Generator::terminate, which, in theory, will be called before
481 // the generators will be constructed again.
482 // We could have used the initialize method for this, but this
483 // would force us into a limited and more complex semantic, see an
484 // example of this in DocParser, and would restrain us further to
485 // the initialize-terminate idiom which is expect to be purged in
486 // the future.
487 HtmlGenerator htmlGenerator{file_resolver};
488 WebXMLGenerator webXMLGenerator{file_resolver};
489 DocBookGenerator docBookGenerator{file_resolver};
490#ifdef QDOC_TEMPLATE_GENERATOR_ENABLED
491 TemplateGenerator templateGenerator{file_resolver};
492#endif
493
495
496 /*
497 Initialize the qdoc database, where all the parsed source files
498 will be stored. The database includes a tree of nodes, which gets
499 built as the source files are parsed. The documentation output is
500 generated by traversing that tree.
501
502 Note: qdocDB() allocates a new instance only if no instance exists.
503 So it is safe to call qdocDB() any time.
504 */
506 qdb->setVersion(config.get(CONFIG_VERSION).asString());
507 /*
508 By default, the only output format is HTML.
509 */
510 const QSet<QString> outputFormats = config.getOutputFormats();
511
513 if (!config.singleExec()) {
514 if (!config.preparing()) {
515 qCDebug(lcQdoc, " loading index files");
516 loadIndexFiles(outputFormats);
517 qCDebug(lcQdoc, " done loading index files");
518 }
519 qdb->newPrimaryTree(project);
520 } else if (config.preparing())
521 qdb->newPrimaryTree(project);
522 else
523 qdb->setPrimaryTree(project);
524
525 // Retrieve the dependencies if loadIndexFiles() was not called
526 if (config.dependModules().isEmpty()) {
527 config.dependModules() = config.get(CONFIG_DEPENDS).asStringList();
528 config.dependModules().removeDuplicates();
529 }
530 qdb->setSearchOrder(config.dependModules());
531
532 // Store the title of the index (landing) page
534 if (root) {
535 QString title{config.get(CONFIG_NAVIGATION + Config::dot + CONFIG_LANDINGPAGE).asString()};
536 root->tree()->setIndexTitle(
537 config.get(CONFIG_NAVIGATION + Config::dot + CONFIG_LANDINGTITLE).asString(std::move(title)));
538 }
539
540
541 std::vector<QByteArray> include_paths{};
542 {
543 auto args = config.getCanonicalPathList(CONFIG_INCLUDEPATHS,
545#ifdef Q_OS_MACOS
546 args.append(Utilities::getInternalIncludePaths(QStringLiteral("clang++")));
547#elif defined(Q_OS_LINUX)
548 args.append(Utilities::getInternalIncludePaths(QStringLiteral("g++")));
549#endif
550
551 for (const auto &path : std::as_const(args)) {
552 if (!path.isEmpty())
553 include_paths.push_back(path.toUtf8());
554 }
555
556 include_paths.erase(std::unique(include_paths.begin(), include_paths.end()),
557 include_paths.end());
558 }
559
560 QList<QByteArray> clang_defines{};
561 {
562 const QStringList config_defines{config.get(CONFIG_DEFINES).asStringList()};
563 for (const QString &def : config_defines) {
564 if (!def.contains(QChar('*'))) {
565 QByteArray tmp("-D");
566 tmp.append(def.toUtf8());
567 clang_defines.append(tmp.constData());
568 }
569 }
570 }
571
572 /*
573 Initialize all the classes and data structures with the
574 qdoc configuration. This is safe to do for each qdocconf
575 file processed, because all the data structures created
576 are either cleared after they have been used, or they
577 are cleared in the terminate() functions below.
578
579 NOTE: This must happen before buildPCH() because during
580 header parsing, auto-generated documentation may be created
581 that uses QDoc markup commands (e.g., \a for parameters).
582 Those commands require Doc::initialize() to have populated
583 the command hash.
584 */
589 Doc::initialize(file_resolver);
590
591 std::optional<PCHFile> pch = std::nullopt;
592 if (config.dualExec() || config.preparing()) {
593 const QString moduleHeader = config.get(CONFIG_MODULEHEADER).asString();
594 pch = buildPCH(
595 QDocDatabase::qdocDB(),
596 moduleHeader.isNull() ? project : moduleHeader,
597 Config::instance().getHeaderFiles(),
598 include_paths,
599 clang_defines,
600 Config::instance().createInclusionPolicy()
601 );
602 }
603
604 ClangCodeParser clangParser(QDocDatabase::qdocDB(), Config::instance(), include_paths, clang_defines, pch);
605 PureDocParser docParser{config.location()};
606
607 if (config.dualExec() || config.preparing()) {
608 QStringList sourceList;
609
610 qCDebug(lcQdoc, "Reading sourcedirs");
611
613 sourceList += config.getAllFiles(CONFIG_HEADERS, CONFIG_HEADERDIRS, excludedDirs,
614 excludedFiles);
615 }
616 sourceList +=
617 config.getAllFiles(CONFIG_SOURCES, CONFIG_SOURCEDIRS, excludedDirs, excludedFiles);
618
619 std::vector<QString> sources{};
620 for (const auto &source : sourceList) {
621 if (!source.contains(QLatin1String("doc/snippets")))
622 sources.emplace_back(source);
623 }
624 /*
625 Find all the qdoc files in the example dirs, and add
626 them to the source files to be parsed.
627 */
628 qCDebug(lcQdoc, "Reading exampledirs");
629 QStringList exampleQdocList = config.getExampleQdocFiles(excludedDirs, excludedFiles);
630 for (const auto &example : exampleQdocList) {
631 if (!example.contains(QLatin1String("doc/snippets")))
632 sources.emplace_back(example);
633 }
634
635 /*
636 Parse each source text file in the set using the appropriate parser and
637 add it to the big tree.
638 */
639 if (config.get(CONFIG_LOGPROGRESS).asBool())
640 qCInfo(lcQdoc) << "Parse source files for" << project;
641
642
643 auto headers = config.getHeaderFiles();
644 CppCodeParser cpp_code_parser(FnCommandParser(qdb, headers, clang_defines, pch));
645
646 SourceFileParser source_file_parser{clangParser, docParser};
647 parseSourceFiles(std::move(sources), source_file_parser, cpp_code_parser);
648
649 if (config.get(CONFIG_LOGPROGRESS).asBool())
650 qCInfo(lcQdoc) << "Source files parsed for" << project;
651 }
652 /*
653 Now the primary tree has been built from all the header and
654 source files. Resolve all the class names, function names,
655 targets, URLs, links, and other stuff that needs resolving.
656 */
657 qCDebug(lcQdoc, "Resolving stuff prior to generating docs");
659
660 /*
661 The primary tree is built and all the stuff that needed
662 resolving has been resolved. Now traverse the tree and
663 generate the documentation output. More than one output
664 format can be requested. The tree is traversed for each
665 one.
666 */
667 qCDebug(lcQdoc, "Generating docs");
668 for (const auto &format : outputFormats) {
669 auto *generator = Generator::generatorForFormat(format);
670 if (generator) {
671 generator->initializeFormat();
672 generator->generateDocs();
673 } else {
674 config.get(CONFIG_OUTPUTFORMATS)
675 .location()
676 .fatal(QStringLiteral("QDoc: Unknown output format '%1'").arg(format));
677 }
678 }
679
681
682 qCDebug(lcQdoc, "Terminating qdoc classes");
684 Utilities::stopDebugging(project);
685
686 logStartEndMessage(QLatin1String("End"), config);
687 QDocDatabase::qdocDB()->setVersion(QString());
694 QDir::setCurrent(config.previousCurrentDir());
695
696 qCDebug(lcQdoc, "qdoc classes terminated");
697}
698
699/*!
700 \internal
701 For each file in \a qdocFiles, first clear the configured module
702 dependencies and then pass the file to processQdocconfFile().
703
704 \sa processQdocconfFile(), singleExecutionMode(), dualExecutionMode()
705*/
706static void clearModuleDependenciesAndProcessQdocconfFile(const QStringList &qdocFiles)
707{
708 for (const auto &file : std::as_const(qdocFiles)) {
709 Config::instance().dependModules().clear();
710 processQdocconfFile(file);
711 }
712}
713
714/*!
715 \internal
716
717 A single QDoc process for prepare and generate phases.
718 The purpose is to first generate all index files for all documentation
719 projects that combined make out the documentation set being generated.
720 This allows QDoc to link to all content contained in all projects, e.g.
721 user-defined types or overview documentation, regardless of the project
722 that content belongs to when generating the final output.
723*/
725{
726 const QStringList qdocFiles = Config::loadMaster(Config::instance().qdocFiles().at(0));
727
728 Config::instance().setQDocPass(Config::Prepare);
729 clearModuleDependenciesAndProcessQdocconfFile(qdocFiles);
730
731 Config::instance().setQDocPass(Config::Generate);
733 clearModuleDependenciesAndProcessQdocconfFile(qdocFiles);
734}
735
736/*!
737 \internal
738
739 Process each .qdocconf-file passed as command line argument(s).
740*/
741static void dualExecutionMode()
742{
743 const QStringList qdocFiles = Config::instance().qdocFiles();
744 clearModuleDependenciesAndProcessQdocconfFile(qdocFiles);
745}
746
748
749int main(int argc, char **argv)
750{
751 QT_USE_NAMESPACE
752
753 // Initialize Qt:
754#ifndef QT_BOOTSTRAPPED
755 // use deterministic hash seed
756 QHashSeed::setDeterministicGlobalSeed();
757#endif
758 QCoreApplication app(argc, argv);
759 app.setApplicationVersion(QLatin1String(QT_VERSION_STR));
760
761 // Instantiate various singletons (used via static methods):
762 /*
763 Create code parsers for the languages to be parsed,
764 and create a tree for C++.
765 */
766 QmlCodeParser qmlParser;
767
768 /*
769 Create code markers for plain text, C++,
770 and QML.
771
772 The plain CodeMarker must be instantiated first because it is used as
773 fallback when the other markers cannot be used.
774
775 Each marker instance is prepended to the CodeMarker::markers list by the
776 base class constructor.
777 */
778 CodeMarker fallbackMarker;
779 CppCodeMarker cppMarker;
780 QmlCodeMarker qmlMarker;
781
782 Config::instance().init("QDoc", app.arguments());
783
784 if (Config::instance().qdocFiles().isEmpty())
785 Config::instance().showHelp();
786
787 if (Config::instance().singleExec()) {
789 } else {
791 }
792
793 // Tidy everything away:
797}
static void initialize()
All the code markers in the static list are initialized here, after the qdoc configuration file has b...
static void terminate()
All the code markers in the static list are terminated here.
virtual void parseSourceFile(const Location &location, const QString &filePath, CppCodeParser &cpp_code_parser)=0
static CodeParser * parserForSourceFile(const QString &filePath)
static void initialize()
All the code parsers in the static list are initialized here, after the qdoc configuration variables ...
static void terminate()
All the code parsers in the static list are terminated here.
bool asBool() const
Returns this config variable as a boolean.
Definition config.cpp:281
The Config class contains the configuration variables for controlling how qdoc produces documentation...
Definition config.h:85
static const QString dot
Definition config.h:169
bool singleExec() const
Definition config.h:457
@ IncludePaths
Definition config.h:105
const Location & location() const
Definition config.h:127
const ExcludedPaths & getExcludedPaths()
Definition config.cpp:1439
bool dualExec() const
Definition config.h:462
bool preparing() const
Definition config.h:188
bool generating() const
Definition config.h:189
bool getDebug() const
Definition config.h:109
static QString overrideOutputDir
Definition config.h:173
CppCodeParser(FnCommandParser &&parser)
void processMetaCommands(const Doc &doc, Node *node)
The topic command has been processed, and now doc and node are passed to this function to get the met...
DocBookGenerator(FileResolver &file_resolver)
Definition doc.h:31
static void initialize(FileResolver &file_resolver)
Definition doc.cpp:357
static void terminate()
All the heap allocated variables are deleted.
Definition doc.cpp:410
Encapsulate the logic that QDoc uses to find files whose path is provided by the user and that are re...
static void initialize()
static void terminate()
static Generator * generatorForFormat(const QString &format)
HtmlGenerator(FileResolver &file_resolver)
The Location class provides a way to mark a location in a file.
Definition location.h:20
static int exitCode()
Returns the error code QDoc should exit with; EXIT_SUCCESS or the number of documentation warnings if...
Definition location.cpp:283
static void initialize()
Gets several parameters from the config, including tab size, program name, and a regular expression t...
Definition location.cpp:336
Location()
Constructs an empty location.
Definition location.cpp:48
static void terminate()
Apparently, all this does is delete the regular expression used for intercepting certain error messag...
Definition location.cpp:482
This class represents a C++ namespace.
Tree * tree() const override
Returns a pointer to the Tree that contains this NamespaceNode.
Represents an output directory that has been verified to exist.
PureDocParser(const Location &location)
This class provides exclusive access to the qdoc database, which consists of a forrest of trees and a...
static void destroyQdocDB()
Destroys the singleton.
static QDocDatabase * qdocDB()
Creates the singleton.
NamespaceNode * primaryTreeRoot()
Returns a pointer to the root node of the primary tree.
void processForest()
This function calls a set of functions for each tree in the forest that has not already been analyzed...
void clearSearchOrder()
void resolveStuff()
Performs several housekeeping tasks prior to generating the documentation.
static void terminate()
Clear the static maps so that subsequent runs don't try to use contents from a previous run.
SourceFileParser(ClangCodeParser &clang_parser, PureDocParser &pure_parser)
static void terminate()
The heap allocated variables are freed here.
static void initialize()
WebXMLGenerator(FileResolver &file_resolver)
#define CONFIG_SOURCES
Definition config.h:436
#define CONFIG_OUTPUTDIR
Definition config.h:417
#define CONFIG_VERSION
Definition config.h:447
#define CONFIG_EXAMPLEDIRS
Definition config.h:379
#define CONFIG_URL
Definition config.h:445
#define CONFIG_INDEXES
Definition config.h:403
#define CONFIG_DEFINES
Definition config.h:373
#define CONFIG_DEPENDS
Definition config.h:374
#define CONFIG_NOLINKERRORS
Definition config.h:416
#define CONFIG_LOGPROGRESS
Definition config.h:408
#define CONFIG_DESCRIPTION
Definition config.h:375
#define CONFIG_IMAGEDIRS
Definition config.h:397
#define CONFIG_PROJECT
Definition config.h:423
#define CONFIG_SOURCEDIRS
Definition config.h:434
#define CONFIG_HEADERS
Definition config.h:388
#define CONFIG_DOCUMENTATIONINHEADERS
Definition config.h:377
#define CONFIG_NAVIGATION
Definition config.h:415
#define CONFIG_LANDINGPAGE
Definition config.h:404
#define CONFIG_OUTPUTFORMATS
Definition config.h:418
#define CONFIG_LANDINGTITLE
Definition config.h:405
#define CONFIG_MODULEHEADER
Definition config.h:413
#define CONFIG_HEADERDIRS
Definition config.h:387
#define CONFIG_INCLUDEPATHS
Definition config.h:399
Combined button and popup list for selecting options.
This namespace holds QDoc-internal utility methods.
Definition utilities.h:20
bool debugging()
Definition utilities.cpp:42
static void generateIndexFile(const Config &config)
Definition main.cpp:313
static void parseSourceFiles(std::vector< QString > &&sources, SourceFileParser &source_file_parser, CppCodeParser &cpp_code_parser)
Definition main.cpp:69
static void singleExecutionMode()
Definition main.cpp:724
void logStartEndMessage(const QLatin1String &startStop, Config &config)
Definition main.cpp:285
static void processQdocconfFile(const QString &fileName)
Processes the qdoc config file fileName.
Definition main.cpp:355
static void clearModuleDependenciesAndProcessQdocconfFile(const QStringList &qdocFiles)
Definition main.cpp:706
static void dualExecutionMode()
Definition main.cpp:741
bool creationTimeBefore(const QFileInfo &fi1, const QFileInfo &fi2)
Definition main.cpp:50
static void loadIndexFiles(const QSet< QString > &formats)
Read some XML indexes containing definitions from other documentation sets.
Definition main.cpp:133
int main(int argc, char *argv[])
[ctor_close]
Processes parser errors and outputs warnings for them.
Definition parsererror.h:20