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