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