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