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
config.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
4#include "config.h"
6#include "utilities.h"
7
8#include <QtCore/qdir.h>
9#include <QtCore/qfile.h>
10#include <QtCore/qfileinfo.h>
11#include <QtCore/qlatin1stringview.h>
12#include <QtCore/qtemporaryfile.h>
13#include <QtCore/qtextstream.h>
14#include <QtCore/qvariant.h>
15#include <QtCore/qregularexpression.h>
16
18
19using namespace Qt::StringLiterals;
20
21QString ConfigStrings::AUTOLINKERRORS = QStringLiteral("autolinkerrors");
22QString ConfigStrings::BUILDVERSION = QStringLiteral("buildversion");
23QString ConfigStrings::CODEINDENT = QStringLiteral("codeindent");
24QString ConfigStrings::CODELANGUAGES = QStringLiteral("codelanguages");
25QString ConfigStrings::CODEPREFIX = QStringLiteral("codeprefix");
26QString ConfigStrings::CODESUFFIX = QStringLiteral("codesuffix");
27QString ConfigStrings::CPPCLASSESPAGE = QStringLiteral("cppclassespage");
28QString ConfigStrings::CPPCLASSESTITLE = QStringLiteral("cppclassestitle");
29QString ConfigStrings::DEFINES = QStringLiteral("defines");
30QString ConfigStrings::DEPENDS = QStringLiteral("depends");
31QString ConfigStrings::DESCRIPTION = QStringLiteral("description");
32QString ConfigStrings::DOCBOOKEXTENSIONS = QStringLiteral("usedocbookextensions");
33QString ConfigStrings::DOCUMENTATIONINHEADERS = QStringLiteral("documentationinheaders");
34QString ConfigStrings::ENDHEADER = QStringLiteral("endheader");
35QString ConfigStrings::EXAMPLEDIRS = QStringLiteral("exampledirs");
36QString ConfigStrings::EXAMPLES = QStringLiteral("examples");
37QString ConfigStrings::EXAMPLESINSTALLPATH = QStringLiteral("examplesinstallpath");
38QString ConfigStrings::EXCLUDEDIRS = QStringLiteral("excludedirs");
39QString ConfigStrings::EXCLUDEFILES = QStringLiteral("excludefiles");
40QString ConfigStrings::EXTRAIMAGES = QStringLiteral("extraimages");
41QString ConfigStrings::FALSEHOODS = QStringLiteral("falsehoods");
42QString ConfigStrings::FORMATTING = QStringLiteral("formatting");
43QString ConfigStrings::HEADERDIRS = QStringLiteral("headerdirs");
44QString ConfigStrings::HEADERS = QStringLiteral("headers");
45QString ConfigStrings::HEADERSCRIPTS = QStringLiteral("headerscripts");
46QString ConfigStrings::HEADERSTYLES = QStringLiteral("headerstyles");
47QString ConfigStrings::HOMEPAGE = QStringLiteral("homepage");
48QString ConfigStrings::HOMETITLE = QStringLiteral("hometitle");
49QString ConfigStrings::IGNOREDIRECTIVES = QStringLiteral("ignoredirectives");
50QString ConfigStrings::IGNORESINCE = QStringLiteral("ignoresince");
51QString ConfigStrings::IGNORETOKENS = QStringLiteral("ignoretokens");
52QString ConfigStrings::IGNOREWORDS = QStringLiteral("ignorewords");
53QString ConfigStrings::IMAGEDIRS = QStringLiteral("imagedirs");
54QString ConfigStrings::IMAGESOUTPUTDIR = QStringLiteral("imagesoutputdir");
55QString ConfigStrings::INCLUDEPATHS = QStringLiteral("includepaths");
56QString ConfigStrings::INCLUDEPRIVATE = QStringLiteral("includeprivate");
57QString ConfigStrings::INTERNALFILEPATTERNS = QStringLiteral("internalfilepatterns");
58QString ConfigStrings::INCLUSIVE = QStringLiteral("inclusive");
59QString ConfigStrings::INDEXES = QStringLiteral("indexes");
60QString ConfigStrings::LANDINGPAGE = QStringLiteral("landingpage");
61QString ConfigStrings::LANDINGTITLE = QStringLiteral("landingtitle");
62QString ConfigStrings::LANGUAGE = QStringLiteral("language");
63QString ConfigStrings::LOCATIONINFO = QStringLiteral("locationinfo");
64QString ConfigStrings::LOGPROGRESS = QStringLiteral("logprogress");
65QString ConfigStrings::LOGWARNINGS = QStringLiteral("logwarnings");
66QString ConfigStrings::LOGWARNINGSDISABLECLIARGS = QStringLiteral("logwarnings.disablecliargs");
67QString ConfigStrings::MACRO = QStringLiteral("macro");
68QString ConfigStrings::MANIFESTMETA = QStringLiteral("manifestmeta");
69QString ConfigStrings::MODULEHEADER = QStringLiteral("moduleheader");
70QString ConfigStrings::NATURALLANGUAGE = QStringLiteral("naturallanguage");
71QString ConfigStrings::NAVIGATION = QStringLiteral("navigation");
72QString ConfigStrings::NOLINKERRORS = QStringLiteral("nolinkerrors");
73QString ConfigStrings::OUTPUTDIR = QStringLiteral("outputdir");
74QString ConfigStrings::OUTPUTFORMATS = QStringLiteral("outputformats");
75QString ConfigStrings::OUTPUTPREFIXES = QStringLiteral("outputprefixes");
76QString ConfigStrings::OUTPUTSUFFIXES = QStringLiteral("outputsuffixes");
77QString ConfigStrings::PARSECPPCOMMENTS = QStringLiteral("parsecppcomments");
78
79/*!
80 \variable ConfigStrings::PIPELINE
81 \internal
82 \brief The \c{pipeline} configuration key, also exposed as \c{CONFIG_PIPELINE}.
83
84 Presence selects the new publication interpretation, even for an empty or
85 invalid selection. The built-in defaults must not declare this key.
86*/
87QString ConfigStrings::PIPELINE = QStringLiteral("pipeline");
88
89QString ConfigStrings::PRELIMINARY = QStringLiteral("preliminary");
90QString ConfigStrings::PRODUCTNAME = QStringLiteral("productname");
91QString ConfigStrings::PROJECT = QStringLiteral("project");
92QString ConfigStrings::PROJECTROOT = QStringLiteral("projectroot");
93
94/*!
95 \variable ConfigStrings::PUBLICATIONLAYOUT
96 \internal
97 \brief The \c{publicationlayout} key, also exposed as \c{CONFIG_PUBLICATIONLAYOUT}.
98
99 Publication resolution uses this key globally or after a format prefix.
100 Its explicit value takes precedence over the deprecated layout alias.
101*/
102QString ConfigStrings::PUBLICATIONLAYOUT = QStringLiteral("publicationlayout");
103
105 QStringLiteral("redirectdocumentationtodevnull");
107 QStringLiteral("reportmissingalttextforimages");
108QString ConfigStrings::QHP = QStringLiteral("qhp");
109QString ConfigStrings::QUOTINGINFORMATION = QStringLiteral("quotinginformation");
110QString ConfigStrings::ROOTDIR = QStringLiteral("rootdir");
111QString ConfigStrings::SCRIPTS = QStringLiteral("scripts");
112QString ConfigStrings::SHOWAUTOGENERATEDDOCS = QStringLiteral("showautogenerateddocs");
113QString ConfigStrings::SHOWINTERNAL = QStringLiteral("showinternal");
114QString ConfigStrings::SINGLEEXEC = QStringLiteral("singleexec");
115QString ConfigStrings::SOURCEDIRS = QStringLiteral("sourcedirs");
116QString ConfigStrings::SOURCEENCODING = QStringLiteral("sourceencoding");
117QString ConfigStrings::SOURCES = QStringLiteral("sources");
118QString ConfigStrings::SPURIOUS = QStringLiteral("spurious");
119QString ConfigStrings::STYLESHEETS = QStringLiteral("stylesheets");
120QString ConfigStrings::SYNTAXHIGHLIGHTING = QStringLiteral("syntaxhighlighting");
121QString ConfigStrings::TABSIZE = QStringLiteral("tabsize");
122QString ConfigStrings::TAGFILE = QStringLiteral("tagfile");
123
124/*!
125 \variable ConfigStrings::TEMPLATEPACKAGE
126 \internal
127 \brief The \c{template.package} key, also exposed as \c{CONFIG_TEMPLATEPACKAGE}.
128
129 Selection diagnoses its presence without pipeline opt-in. Reading this key
130 does not load or validate a package; package resolution is separate work.
131*/
132QString ConfigStrings::TEMPLATEPACKAGE = QStringLiteral("template.package");
133
134QString ConfigStrings::TIMESTAMPS = QStringLiteral("timestamps");
135QString ConfigStrings::TOCTITLES = QStringLiteral("toctitles");
136QString ConfigStrings::TRADEMARKSPAGE = QStringLiteral("trademarkspage");
137QString ConfigStrings::URL = QStringLiteral("url");
138QString ConfigStrings::USEALTTEXTASTITLE = QStringLiteral("usealttextastitle");
139QString ConfigStrings::VERSION = QStringLiteral("version");
140QString ConfigStrings::VERSIONSYM = QStringLiteral("versionsym");
141QString ConfigStrings::FILEEXTENSIONS = QStringLiteral("fileextensions");
142QString ConfigStrings::IMAGEEXTENSIONS = QStringLiteral("imageextensions");
143QString ConfigStrings::QMLTYPESPAGE = QStringLiteral("qmltypespage");
144QString ConfigStrings::QMLTYPESTITLE = QStringLiteral("qmltypestitle");
145QString ConfigStrings::WARNABOUTMISSINGIMAGES = QStringLiteral("warnaboutmissingimages");
146QString ConfigStrings::WARNABOUTMISSINGPROJECTFILES = QStringLiteral("warnaboutmissingprojectfiles");
147QString ConfigStrings::WARNINGLIMIT = QStringLiteral("warninglimit");
148
149/*!
150 An entry in a stack, where each entry is a list
151 of string values.
152 */
154{
155public:
156 void open();
157 void close();
158
161};
163
164/*!
165 Start accumulating values in a list by appending an empty
166 string to the list.
167 */
169{
170 next.append(QString());
171}
172
173/*!
174 Stop accumulating values and append the list of accumulated
175 values to the complete list of accumulated values.
176
177 */
179{
180 accum += next;
181 next.clear();
182}
183
184/*!
185 \class MetaStack
186
187 This class maintains a stack of values of config file variables.
188*/
190{
191public:
193
194 void process(QChar ch, const Location &location);
196};
197
198/*!
199 The default constructor pushes a new stack entry and
200 opens it.
201 */
203{
204 push(MetaStackEntry());
205 top().open();
206}
207
208/*!
209 Processes the character \a ch using the \a location.
210 It really just builds up a name by appending \a ch to
211 it.
212 */
213void MetaStack::process(QChar ch, const Location &location)
214{
215 if (ch == QLatin1Char('{')) {
216 push(MetaStackEntry());
217 top().open();
218 } else if (ch == QLatin1Char('}')) {
219 if (size() == 1)
220 location.fatal(QStringLiteral("Unexpected '}'"));
221
222 top().close();
223 const QStringList suffixes = pop().accum;
224 const QStringList prefixes = top().next;
225
226 top().next.clear();
227 for (const auto &prefix : prefixes) {
228 for (const auto &suffix : suffixes)
229 top().next << prefix + suffix;
230 }
231 } else if (ch == QLatin1Char(',') && size() > 1) {
232 top().close();
233 top().open();
234 } else {
235 for (QString &topNext : top().next)
236 topNext += ch;
237 }
238}
239
240/*!
241 Returns the accumulated string values.
242 */
244{
245 if (size() > 1)
246 location.fatal(QStringLiteral("Missing '}'"));
247
248 top().close();
249 return top().accum;
250}
251
252const QString Config::dot = QLatin1String(".");
253bool Config::m_debug = false;
254bool Config::m_atomsDump = false;
255bool Config::generateExamples = true;
256bool Config::parseCppComments = true;
260QMap<QString, QString> Config::m_extractedDirs;
261QStack<QString> Config::m_workingDirs;
262QMap<QString, QStringList> Config::m_includeFilesMap;
263
264/*!
265 \class ConfigVar
266 \brief contains all the information for a single config variable in a
267 .qdocconf file.
268*/
269
270/*!
271 Returns this configuration variable as a string.
272
273 If the variable is not defined, returns \a defaultString.
274
275 \note By default, \a defaultString is a null string.
276 This allows determining whether a configuration variable is
277 undefined (returns a null string) or defined as empty
278 (returns a non-null, empty string).
279*/
280QString ConfigVar::asString(const QString defaultString) const
281{
282 if (m_name.isEmpty())
283 return defaultString;
284
285 QString result(""); // an empty but non-null string
286 for (const auto &value : std::as_const(m_values)) {
287 if (!result.isEmpty() && !result.endsWith(QChar('\n')))
288 result.append(QChar(' '));
289 result.append(value.m_value);
290 }
291 return result;
292}
293
294/*!
295 Returns this config variable as a string list.
296*/
298{
299 QStringList result;
300 for (const auto &value : std::as_const(m_values))
301 result << value.m_value;
302 return result;
303}
304
305/*!
306 Returns this config variable as a string set.
307*/
309{
310 const auto &stringList = asStringList();
311 return QSet<QString>(stringList.cbegin(), stringList.cend());
312}
313
314/*!
315 Returns this config variable as a boolean.
316*/
317bool ConfigVar::asBool() const
318{
319 return QVariant(asString()).toBool();
320}
321
322/*!
323 Returns this configuration variable as an integer; iterates
324 through the string list, interpreting each
325 string in the list as an integer and adding it to a total sum.
326
327 Returns 0 if this variable is defined as empty, and
328 -1 if it's is not defined.
329 */
330int ConfigVar::asInt() const
331{
332 const QStringList strs = asStringList();
333 if (strs.isEmpty())
334 return -1;
335
336 int sum = 0;
337 for (const auto &str : strs)
338 sum += str.toInt();
339 return sum;
340}
341
342/*!
343 Appends values to this ConfigVar, and adjusts the ExpandVar
344 parameters so that they continue to refer to the correct values.
345*/
346void ConfigVar::append(const ConfigVar &other)
347{
348 m_expandVars << other.m_expandVars;
349 QList<ExpandVar>::Iterator it = m_expandVars.end();
350 it -= other.m_expandVars.size();
351 std::for_each(it, m_expandVars.end(), [this](ExpandVar &v) {
352 v.m_valueIndex += m_values.size();
353 });
354 m_values << other.m_values;
355 m_location = other.m_location;
356}
357
358/*!
359 \class Config
360 \brief The Config class contains the configuration variables
361 for controlling how qdoc produces documentation.
362
363 Its load() function reads, parses, and processes a qdocconf file.
364 */
365
366/*!
367 \enum Config::PathFlags
368
369 Flags used for retrieving canonicalized paths from Config.
370
371 \value Validate
372 Issue a warning for paths that do not exist and
373 remove them from the returned list.
374
375 \value IncludePaths
376 Assume the variable contains include paths with
377 prefixes such as \c{-I} that are to be removed
378 before canonicalizing and then re-inserted.
379
380 \omitvalue None
381
382 \sa getCanonicalPathList()
383*/
384
385/*!
386 Initializes the Config with \a programName and sets all
387 internal state variables to either default values or to ones
388 defined in command line arguments \a args.
389 */
390void Config::init(const QString &programName, const QStringList &args)
391{
392 m_prog = programName;
393 processCommandLineOptions(args);
394 reset();
395}
396
398{
399 clear();
400}
401
402/*!
403 Clears the location and internal maps for config variables.
404 */
406{
407 m_location = Location();
408 m_configVars.clear();
409 m_includeFilesMap.clear();
410 m_excludedPaths.reset();
411 m_sourceLink.reset();
412 m_internalFilePatterns.reset();
413}
414
415/*!
416 Resets the Config instance and loads the default values. A subset
417 of command line options are published as configuration variables.
418 */
420{
421 clear();
422
423 // Load default values
424 load(Location(), u":/qdoc/config/defaults.qdocconf"_s);
425
426 // Publish options from the command line as config variables
427 const auto setListFlag = [this](const QString &key, bool test) {
428 setStringList(key, QStringList(test ? QStringLiteral("true") : QStringLiteral("false")));
429 };
430#define SET(opt, test) setListFlag(opt, m_parser.isSet(m_parser.test))
431 SET(CONFIG_SYNTAXHIGHLIGHTING, highlightingOption);
432 SET(CONFIG_SINGLEEXEC, singleExecOption);
433 SET(CONFIG_REDIRECTDOCUMENTATIONTODEVNULL, redirectDocumentationToDevNullOption);
434 SET(CONFIG_AUTOLINKERRORS, autoLinkErrorsOption);
435#undef SET
436 setListFlag(CONFIG_SHOWINTERNAL,
437 m_parser.isSet(m_parser.showInternalOption)
438 || qEnvironmentVariableIsSet("QDOC_SHOW_INTERNAL"));
439 m_showInternal = m_configVars.value(CONFIG_SHOWINTERNAL).asBool();
440 setListFlag(CONFIG_NOLINKERRORS,
441 m_parser.isSet(m_parser.noLinkErrorsOption)
442 || qEnvironmentVariableIsSet("QDOC_NOLINKERRORS"));
443
444 // CONFIG_DEFINES and CONFIG_INCLUDEPATHS are set in load()
445}
446
447/*!
448 Loads and parses the qdoc configuration file \a fileName.
449 If a previous project was loaded, this function first resets the
450 Config instance. Then it calls the other load() function, which
451 does the loading, parsing, and processing of the configuration file.
452 */
453void Config::load(const QString &fileName)
454{
455 // Reset if a previous project was loaded
456 if (m_configVars.contains(CONFIG_PROJECT))
457 reset();
458
459 load(Location(), fileName);
460 if (m_location.isEmpty())
461 m_location = Location(fileName);
462 else
463 m_location.setEtc(true);
464
465 // Resolve variables that are interpreted as paths
466 QString varName{CONFIG_URL + dot + CONFIG_SOURCES + dot + CONFIG_ROOTDIR};
467 setStringList(varName, getCanonicalPathList(varName, Validate));
468
469 expandVariables();
470
471 // Add defines and includepaths from command line to their
472 // respective configuration variables. Values set here are
473 // always added to what's defined in configuration file.
474 insertStringList(CONFIG_DEFINES, m_defines);
475 insertStringList(CONFIG_INCLUDEPATHS, m_includePaths);
476
477 // Prefetch values that are used internally
478 m_exampleFiles = getCanonicalPathList(CONFIG_EXAMPLES);
479 m_exampleDirs = getCanonicalPathList(CONFIG_EXAMPLEDIRS);
480 m_reportMissingAltTextForImages =
481 m_configVars.value(CONFIG_REPORTMISSINGALTTEXTFORIMAGES).asBool();
482
483 if (!m_parser.isSet(m_parser.showInternalOption) && !qEnvironmentVariableIsSet("QDOC_SHOW_INTERNAL"))
484 m_showInternal = m_configVars.value(CONFIG_SHOWINTERNAL).asBool();
485
486 // Determine whether C++ documentation comments need to be parsed.
487 // The explicit 'parsecppcomments' variable takes precedence; otherwise
488 // an empty 'moduleheader' is the legacy way to signal no C++ documentation.
489 if (m_configVars.contains(CONFIG_PARSECPPCOMMENTS)) {
490 parseCppComments = m_configVars.value(CONFIG_PARSECPPCOMMENTS).asBool();
491 } else {
492 const auto &moduleHeader = m_configVars.value(CONFIG_MODULEHEADER).asString();
493 parseCppComments = !moduleHeader.isEmpty() || moduleHeader.isNull();
494 }
495}
496
497/*!
498 Expands other config variables referred to in all stored ConfigVars.
499*/
500void Config::expandVariables()
501{
502 for (auto &configVar : m_configVars) {
503 for (auto it = configVar.m_expandVars.crbegin(); it != configVar.m_expandVars.crend(); ++it) {
504 Q_ASSERT(it->m_valueIndex < configVar.m_values.size());
505 const QString &key = it->m_var;
506 const auto &refVar = m_configVars.value(key);
507 if (refVar.m_name.isEmpty()) {
508 configVar.m_location.fatal(
509 QStringLiteral("Environment or configuration variable '%1' undefined")
510 .arg(it->m_var));
511 } else if (!refVar.m_expandVars.empty()) {
512 configVar.m_location.fatal(
513 QStringLiteral("Nested variable expansion not allowed"),
514 QStringLiteral("When expanding '%1' at %2:%3")
515 .arg(refVar.m_name, refVar.m_location.filePath(),
516 QString::number(refVar.m_location.lineNo())));
517 }
518 QString expanded;
519 if (it->m_delim.isNull())
520 expanded = m_configVars.value(key).asStringList().join(QString());
521 else
522 expanded = m_configVars.value(key).asStringList().join(it->m_delim);
523 configVar.m_values[it->m_valueIndex].m_value.insert(it->m_index, expanded);
524 }
525 configVar.m_expandVars.clear();
526 }
527}
528
529/*!
530 Sets the \a values of a configuration variable \a var from a string list.
531 */
532void Config::setStringList(const QString &var, const QStringList &values)
533{
534 m_configVars.insert(var, ConfigVar(var, values, QDir::currentPath()));
535}
536
537/*!
538 Adds the \a values from a string list to the configuration variable \a var.
539 Existing value(s) are kept.
540*/
541void Config::insertStringList(const QString &var, const QStringList &values)
542{
543 m_configVars[var].append(ConfigVar(var, values, QDir::currentPath()));
544}
545
546/*!
547 Process and store variables from the command line.
548 */
549void Config::processCommandLineOptions(const QStringList &args)
550{
551 m_parser.process(args);
552
553 m_defines = m_parser.values(m_parser.defineOption);
554 m_dependModules = m_parser.values(m_parser.dependsOption);
555 setIndexDirs();
556 setIncludePaths();
557
558 generateExamples = !m_parser.isSet(m_parser.noExamplesOption);
559 if (m_parser.isSet(m_parser.installDirOption))
560 installDir = m_parser.value(m_parser.installDirOption);
561 if (m_parser.isSet(m_parser.outputDirOption))
562 overrideOutputDir = QDir(m_parser.value(m_parser.outputDirOption)).absolutePath();
563
564 const auto outputFormats = m_parser.values(m_parser.outputFormatOption);
565 for (const auto &format : outputFormats)
566 overrideOutputFormats.insert(format);
567 m_debug = m_parser.isSet(m_parser.debugOption) || qEnvironmentVariableIsSet("QDOC_DEBUG");
568 m_atomsDump = m_parser.isSet(m_parser.atomsDumpOption);
569 m_showInternal = m_parser.isSet(m_parser.showInternalOption)
570 || qEnvironmentVariableIsSet("QDOC_SHOW_INTERNAL");
571
572 if (m_parser.isSet(m_parser.prepareOption))
573 m_qdocPass = Prepare;
574 if (m_parser.isSet(m_parser.generateOption))
575 m_qdocPass = Generate;
576 if (m_debug || m_parser.isSet(m_parser.logProgressOption))
577 setStringList(CONFIG_LOGPROGRESS, QStringList("true"));
578 if (m_parser.isSet(m_parser.timestampsOption))
579 setStringList(CONFIG_TIMESTAMPS, QStringList("true"));
580 if (m_parser.isSet(m_parser.useDocBookExtensions))
581 setStringList(CONFIG_DOCBOOKEXTENSIONS, QStringList("true"));
582}
583
584void Config::setIncludePaths()
585{
586 QDir currentDir = QDir::current();
587 const auto addIncludePaths = [this, currentDir](const char *flag, const QStringList &paths) {
588 for (const auto &path : paths)
589 m_includePaths << currentDir.absoluteFilePath(path).insert(0, flag);
590 };
591
592 addIncludePaths("-I", m_parser.values(m_parser.includePathOption));
593#ifdef QDOC_PASS_ISYSTEM
594 addIncludePaths("-isystem", m_parser.values(m_parser.includePathSystemOption));
595#endif
596 addIncludePaths("-F", m_parser.values(m_parser.frameworkOption));
597}
598
599/*!
600 Stores paths from --indexdir command line option(s).
601 */
602void Config::setIndexDirs()
603{
604 m_indexDirs = m_parser.values(m_parser.indexDirOption);
605 auto it = std::remove_if(m_indexDirs.begin(), m_indexDirs.end(),
606 [](const QString &s) { return !QFile::exists(s); });
607
608 std::for_each(it, m_indexDirs.end(), [](const QString &s) {
609 qCWarning(lcQdoc) << "Cannot find index directory: " << s;
610 });
611 m_indexDirs.erase(it, m_indexDirs.end());
612}
613
614/*!
615 Function to return the correct outputdir for the output \a format.
616 If \a format is not specified, defaults to 'HTML'.
617 outputdir can be set using the qdocconf or the command-line
618 variable --outputdir.
619 */
620QString Config::getOutputDir(const QString &format) const
621{
622 QString t;
623 if (overrideOutputDir.isNull())
624 t = m_configVars.value(CONFIG_OUTPUTDIR).asString();
625 else
626 t = overrideOutputDir;
627 if (m_configVars.value(CONFIG_SINGLEEXEC).asBool()) {
628 QString project = m_configVars.value(CONFIG_PROJECT).asString();
629 t += QLatin1Char('/') + project.toLower();
630 }
631 if (m_configVars.value(format + Config::dot + "nosubdirs").asBool()) {
632 QString singleOutputSubdir = m_configVars.value(format + Config::dot + "outputsubdir").asString();
633 if (singleOutputSubdir.isEmpty())
634 singleOutputSubdir = "html";
635 t += QLatin1Char('/') + singleOutputSubdir;
636 }
637 return QDir::cleanPath(t);
638}
639
640/*!
641 \fn bool Config::contains(const QString &var) const
642 \internal
643
644 Returns \c true if \a var exists in the configuration, including defaults
645 and programmatically inserted values. An explicitly empty declaration
646 counts as present. This tests presence, not the value's truth or validity.
647
648 Unlike the fallback returned by \c{get()}, this query distinguishes an
649 absent variable without inserting a default-constructed entry.
650*/
651
652/*!
653 \fn QStringList Config::variableNames() const
654 \internal
655
656 Returns the configuration variable names in sorted order, including defaults
657 and programmatic declarations. Reading the names does not insert undefined
658 variables. Publication resolution uses this list to find recognized settings
659 even when their formats do not occur in the effective request.
660*/
661
662/*!
663 Function to return the correct outputformats.
664 outputformats can be set using the qdocconf or the command-line
665 variable --outputformat.
666 */
668{
669 if (overrideOutputFormats.isEmpty())
670 return m_configVars.value(CONFIG_OUTPUTFORMATS).asStringSet();
671 else
672 return overrideOutputFormats;
673}
674
675// TODO: [late-canonicalization][pod-configuration]
676// The canonicalization for paths is done at the time where they are
677// required, and done each time they are requested.
678// Instead, config should be parsed to an intermediate format that is
679// a POD type that already contains canonicalized representations for
680// each element.
681// Those representations should provide specific guarantees about
682// their format and be representable at the API boundaries.
683//
684// This would ensure that the correct canonicalization is always
685// applied, is applied only once and that dependent sub-logics can be
686// written in a way that doesn't require branching or futher
687// canonicalization.
688
689/*!
690 Returns a path list where all paths from the config variable \a var
691 are canonicalized. If \a flags contains \c Validate, outputs a warning
692 for invalid paths. The \c IncludePaths flag is used as a hint to strip
693 away potential prefixes found in include paths before attempting to
694 canonicalize.
695 */
696QStringList Config::getCanonicalPathList(const QString &var, PathFlags flags) const
697{
698 QStringList result;
699 const auto &configVar = m_configVars.value(var);
700
701 for (const auto &value : configVar.m_values) {
702 const QString &currentPath = value.m_path;
703 QString rawValue = value.m_value.simplified();
704 QString prefix;
705
706 if (flags & IncludePaths) {
707 const QStringList prefixes = QStringList()
708 << QLatin1String("-I")
709 << QLatin1String("-F")
710 << QLatin1String("-isystem");
711 const auto end = std::end(prefixes);
712 const auto it =
713 std::find_if(std::begin(prefixes), end,
714 [&rawValue](const QString &p) {
715 return rawValue.startsWith(p);
716 });
717 if (it != end) {
718 prefix = *it;
719 rawValue.remove(0, it->size());
720 if (rawValue.isEmpty())
721 continue;
722 } else {
723 prefix = prefixes[0]; // -I as default
724 }
725 }
726
727 QDir dir(rawValue.trimmed());
728 const QString path = dir.path();
729
730 if (dir.isRelative())
731 dir.setPath(currentPath + QLatin1Char('/') + path);
732 if ((flags & Validate) && !QFileInfo::exists(dir.path()))
733 configVar.m_location.warning(QStringLiteral("Cannot find file or directory: %1").arg(path));
734 else {
735 const QString canonicalPath = dir.canonicalPath();
736 if (!canonicalPath.isEmpty())
737 result.append(prefix + canonicalPath);
738 else if (path.contains(QLatin1Char('*')) || path.contains(QLatin1Char('?')))
739 result.append(path);
740 else
741 qCDebug(lcQdoc) <<
742 qUtf8Printable(QStringLiteral("%1: Ignored nonexistent path \'%2\'")
743 .arg(configVar.m_location.toString(), rawValue));
744 }
745 }
746 return result;
747}
748
749/*!
750 Calls getRegExpList() with the control variable \a var and
751 iterates through the resulting list of regular expressions,
752 concatenating them with extra characters to form a single
753 QRegularExpression, which is then returned.
754
755 \sa getRegExpList()
756 */
757QRegularExpression Config::getRegExp(const QString &var) const
758{
759 QString pattern;
760 const auto subRegExps = getRegExpList(var);
761
762 for (const auto &regExp : subRegExps) {
763 if (!regExp.isValid())
764 return regExp;
765 if (!pattern.isEmpty())
766 pattern += QLatin1Char('|');
767 pattern += QLatin1String("(?:") + regExp.pattern() + QLatin1Char(')');
768 }
769 if (pattern.isEmpty())
770 pattern = QLatin1String("$x"); // cannot match
771 return QRegularExpression(pattern);
772}
773
774/*!
775 Looks up the configuration variable \a var in the string list
776 map, converts the string list to a list of regular expressions,
777 and returns it.
778 */
779QList<QRegularExpression> Config::getRegExpList(const QString &var) const
780{
781 const QStringList strs = m_configVars.value(var).asStringList();
782 QList<QRegularExpression> regExps;
783 for (const auto &str : strs)
784 regExps += QRegularExpression(str);
785 return regExps;
786}
787
788/*!
789 This function is slower than it could be. What it does is
790 find all the keys that begin with \a var + dot and return
791 the matching keys in a set, stripped of the matching prefix
792 and dot.
793 */
794QSet<QString> Config::subVars(const QString &var) const
795{
796 QSet<QString> result;
797 QString varDot = var + QLatin1Char('.');
798 for (auto it = m_configVars.constBegin(); it != m_configVars.constEnd(); ++it) {
799 if (it.key().startsWith(varDot)) {
800 QString subVar = it.key().mid(varDot.size());
801 int dot = subVar.indexOf(QLatin1Char('.'));
802 if (dot != -1)
803 subVar.truncate(dot);
804 result.insert(subVar);
805 }
806 }
807 return result;
808}
809
810/*!
811 Searches for a path to \a fileName in 'sources', 'sourcedirs', and
812 'exampledirs' config variables and returns a full path to the first
813 match found. If the file is not found, returns an empty string.
814 */
815QString Config::getIncludeFilePath(const QString &fileName) const
816{
817 QString ext = QFileInfo(fileName).suffix();
818
819 if (!m_includeFilesMap.contains(ext)) {
820 QStringList result = getCanonicalPathList(CONFIG_SOURCES);
821 result.erase(std::remove_if(result.begin(), result.end(),
822 [&](const QString &s) { return !s.endsWith(ext); }),
823 result.end());
824 const QStringList dirs =
825 getCanonicalPathList(CONFIG_SOURCEDIRS) +
826 getCanonicalPathList(CONFIG_EXAMPLEDIRS);
827
828 for (const auto &dir : dirs)
829 result += getFilesHere(dir, "*." + ext, location());
830 result.removeDuplicates();
831 m_includeFilesMap.insert(ext, result);
832 }
833 const QStringList &paths = (*m_includeFilesMap.find(ext));
834 QString match = fileName;
835 if (!match.startsWith('/'))
836 match.prepend('/');
837 for (const auto &path : paths) {
838 if (path.endsWith(match))
839 return path;
840 }
841 return QString();
842}
843
844/*!
845 Builds and returns a list of file pathnames for the file
846 type specified by \a filesVar (e.g. "headers" or "sources").
847 The files are found in the directories specified by
848 \a dirsVar, and they are filtered by \a defaultNameFilter
849 if a better filter can't be constructed from \a filesVar.
850 The directories in \a excludedDirs are avoided. The files
851 in \a excludedFiles are not included in the return list.
852 */
853QStringList Config::getAllFiles(const QString &filesVar, const QString &dirsVar,
854 const QSet<QString> &excludedDirs,
855 const QSet<QString> &excludedFiles)
856{
857 QStringList result = getCanonicalPathList(filesVar, Validate);
858 const QStringList dirs = getCanonicalPathList(dirsVar, Validate);
859
860 const QString nameFilter = m_configVars.value(filesVar + dot + CONFIG_FILEEXTENSIONS).asString();
861
862 for (const auto &dir : dirs)
863 result += getFilesHere(dir, nameFilter, location(), excludedDirs, excludedFiles);
864 return result;
865}
866
867QStringList Config::getExampleQdocFiles(const QSet<QString> &excludedDirs,
868 const QSet<QString> &excludedFiles)
869{
870 QStringList result;
871 const QStringList dirs = getCanonicalPathList("exampledirs");
872 const QString nameFilter = " *.qdoc";
873
874 for (const auto &dir : dirs)
875 result += getFilesHere(dir, nameFilter, location(), excludedDirs, excludedFiles);
876 return result;
877}
878
879QStringList Config::getExampleImageFiles(const QSet<QString> &excludedDirs,
880 const QSet<QString> &excludedFiles)
881{
882 QStringList result;
883 const QStringList dirs = getCanonicalPathList("exampledirs");
884 const QString nameFilter = m_configVars.value(CONFIG_EXAMPLES + dot + CONFIG_IMAGEEXTENSIONS).asString();
885
886 for (const auto &dir : dirs)
887 result += getFilesHere(dir, nameFilter, location(), excludedDirs, excludedFiles);
888 return result;
889}
890
891// TODO: [misplaced-logic][examples][pod-configuration]
892// The definition of how an example is structured and how to find its
893// components should not be part of Config or, for that matter,
894// CppCodeParser, which is the actual caller of this method.
895// Move this method to a more appropriate place as soon as a suitable
896// place is available for it.
897
898/*!
899 Returns the path to the project file for \a examplePath, or an empty string
900 if no project file was found.
901 */
902QString Config::getExampleProjectFile(const QString &examplePath)
903{
904 QFileInfo fileInfo(examplePath);
905 QStringList validNames;
906 validNames << QLatin1String("CMakeLists.txt")
907 << fileInfo.fileName() + QLatin1String(".pro")
908 << fileInfo.fileName() + QLatin1String(".qmlproject")
909 << fileInfo.fileName() + QLatin1String(".pyproject")
910 << QLatin1String("qbuild.pro"); // legacy
911
912 QString projectFile;
913
914 for (const auto &name : std::as_const(validNames)) {
915 projectFile = Config::findFile(Location(), m_exampleFiles, m_exampleDirs,
916 examplePath + QLatin1Char('/') + name);
917 if (!projectFile.isEmpty())
918 return projectFile;
919 }
920
921 return projectFile;
922}
923
924// TODO: [pod-configuration]
925// Remove findFile completely from the configuration.
926// External usages of findFile were already removed but a last caller
927// of this method exists internally to Config in
928// `getExampleProjectFile`.
929// That method has to be removed at some point and this method should
930// go with it.
931// Do notice that FileResolver is the replacement for findFile but it
932// is designed, for now, with a scope that does only care about the
933// usages of findFile that are outside the Config class.
934// More specifically, it was designed to replace only the uses of
935// findFile that deal with user provided queries or queries related to
936// that.
937// The logic that is used internally in Config is the same, but has a
938// different conceptual meaning.
939// When findFile is permanently removed, it must be considered whether
940// FileResolver itself should be used for the same logic or not.
941
942/*!
943 \a fileName is the path of the file to find.
944
945 \a files and \a dirs are the lists where we must find the
946 components of \a fileName.
947
948 \a location is used for obtaining the file and line numbers
949 for report qdoc errors.
950 */
951QString Config::findFile(const Location &location, const QStringList &files,
952 const QStringList &dirs, const QString &fileName,
953 QString *userFriendlyFilePath)
954{
955 if (fileName.isEmpty() || fileName.startsWith(QLatin1Char('/'))) {
956 if (userFriendlyFilePath)
957 *userFriendlyFilePath = fileName;
958 return fileName;
959 }
960
961 QFileInfo fileInfo;
962 QStringList components = fileName.split(QLatin1Char('?'));
963 QString firstComponent = components.first();
964
965 for (const auto &file : files) {
966 if (file == firstComponent || file.endsWith(QLatin1Char('/') + firstComponent)) {
967 fileInfo.setFile(file);
968 if (!fileInfo.exists())
969 location.fatal(QStringLiteral("File '%1' does not exist").arg(file));
970 break;
971 }
972 }
973
974 if (fileInfo.fileName().isEmpty()) {
975 for (const auto &dir : dirs) {
976 fileInfo.setFile(QDir(dir), firstComponent);
977 if (fileInfo.exists())
978 break;
979 }
980 }
981
982 if (userFriendlyFilePath)
983 userFriendlyFilePath->clear();
984 if (!fileInfo.exists())
985 return QString();
986
987 // <<REMARK: This is actually dead code. It is unclear what it tries
988 // to do and why but its usage is unnecessary in the current
989 // codebase.
990 // Indeed, the whole concept of the "userFriendlyFilePath" is
991 // removed for file searching.
992 // It will be removed directly with the whole of findFile, but it
993 // should not be considered anymore until then.
994 if (userFriendlyFilePath) {
995 for (auto c = components.constBegin();;) {
996 bool isArchive = (c != components.constEnd() - 1);
997 userFriendlyFilePath->append(*c);
998
999 if (isArchive) {
1000 QString extracted = m_extractedDirs[fileInfo.filePath()];
1001
1002 ++c;
1003 fileInfo.setFile(QDir(extracted), *c);
1004 } else {
1005 break;
1006 }
1007
1008 userFriendlyFilePath->append(QLatin1Char('?'));
1009 }
1010 }
1011 // REMARK>>
1012
1013 return fileInfo.filePath();
1014}
1015
1016// TODO: [pod-configuration]
1017// An intermediate representation for the configuration should only
1018// contain data that will later be destructured into subsystem that
1019// care about specific subsets of the configuration and can carry that
1020// information with them, uniquely.
1021// Remove copyFile, moving it into whatever will have the unique
1022// resposability of knowing how to build an output directory for a
1023// QDoc execution.
1024// Should copy file being used for not only copying file to the build
1025// output directory, split its responsabilities into smaller elements
1026// instead of forcing the logic together.
1027
1028/*!
1029 Copies the \a sourceFilePath to the file name constructed by
1030 concatenating \a targetDirPath and the file name from the
1031 \a userFriendlySourceFilePath. \a location is for identifying
1032 the file and line number where a qdoc error occurred. The
1033 constructed output file name is returned.
1034 */
1035QString Config::copyFile(const Location &location, const QString &sourceFilePath,
1036 const QString &userFriendlySourceFilePath, const QString &targetDirPath)
1037{
1038 // TODO: A copying operation should only be performed on files
1039 // that we assume to be available. Ensure that this is true at the
1040 // API boundary and bubble up the error checking and reporting to
1041 // call-site users. Possibly this will be as simple as
1042 // ResolvedFile, but could not be done at the time of the introduction of
1043 // that type as we first need to encapsulate the logic for
1044 // copying files into an appropriate subsystem and have a better
1045 // understanding of call-site usages.
1046
1047 QFile inFile(sourceFilePath);
1048 if (!inFile.open(QFile::ReadOnly)) {
1049 location.warning(QStringLiteral("Cannot open input file for copy: '%1': %2")
1050 .arg(sourceFilePath, inFile.errorString()));
1051 return QString();
1052 }
1053
1054 // TODO: [non-canonical-representation]
1055 // Similar to other part of QDoc, we do a series of non-intuitive
1056 // checks to canonicalize some multi-format parameter into
1057 // something we can use.
1058 // Understand which of those formats are actually in use and
1059 // provide a canonicalized version that can be requested at the
1060 // API boundary to ensure that correct formatting is used.
1061 // If possible, gradually bubble up the canonicalization until a
1062 // single entry-point in the program exists where the
1063 // canonicalization can be processed to avoid complicating
1064 // intermediate steps.
1065 // ADDENDUM 1: At least one usage of this seems to depend on the
1066 // processing done for files coming from
1067 // Generator::copyTemplateFile, which are expressed as absolute
1068 // paths. This seems to be the only usage that is currently
1069 // needed, hence a temporary new implementation is provided that
1070 // only takes this case into account.
1071 // Do notice that we assume that in this case we always want a
1072 // flat structure, that is, we are copying the file as a direct
1073 // child of the target directory.
1074 // Nonetheless, it is possible that this case will not be needed,
1075 // such that it can be removed later on, or that it will be nedeed
1076 // in multiple places such that an higher level interface for it
1077 // should be provided.
1078 // Furthermoe, it might be possible that there is an edge case
1079 // that is now not considered, as it is unknown, that was
1080 // considered before.
1081 // As it is now unclear what kind of paths are used here, what
1082 // format they have, why they are used and why they have some
1083 // specific format, further processing is avoided but a more
1084 // torough overview of what should is needed must be done when
1085 // more information are gathered and this function is extracted
1086 // away from config.
1087
1088 QString outFileName{userFriendlySourceFilePath};
1089 QFileInfo outFileNameInfo{userFriendlySourceFilePath};
1090 if (outFileNameInfo.isAbsolute())
1091 outFileName = outFileNameInfo.fileName();
1092
1093 outFileName = targetDirPath + "/" + outFileName;
1094 QDir targetDir(targetDirPath);
1095 if (!targetDir.exists())
1096 targetDir.mkpath(".");
1097
1098 QFile outFile(outFileName);
1099 if (!outFile.open(QFile::WriteOnly)) {
1100 // TODO: [uncrentralized-warning]
1101 location.warning(QStringLiteral("Cannot open output file for copy: '%1': %2")
1102 .arg(outFileName, outFile.errorString()));
1103 return QString();
1104 }
1105
1106 // TODO: There shouldn't be any particular advantage to copying
1107 // the file by readying its content and writing it compared to
1108 // asking the underlying system to do the copy for us.
1109 // Consider simplifying this part by avoiding doing the manual
1110 // work ourselves.
1111
1112 char buffer[1024];
1113 qsizetype len;
1114 while ((len = inFile.read(buffer, sizeof(buffer))) > 0)
1115 outFile.write(buffer, len);
1116 return outFileName;
1117}
1118
1119/*!
1120 Finds the largest unicode digit in \a value in the range
1121 1..7 and returns it.
1122 */
1123int Config::numParams(const QString &value)
1124{
1125 int max = 0;
1126 for (int i = 0; i != value.size(); ++i) {
1127 uint c = value[i].unicode();
1128 if (c > 0 && c < 8)
1129 max = qMax(max, static_cast<int>(c));
1130 }
1131 return max;
1132}
1133
1134/*!
1135 Returns \c true if \a ch is a letter, number, '_', '.',
1136 '{', '}', or ','.
1137 */
1138bool Config::isMetaKeyChar(QChar ch)
1139{
1140 return ch.isLetterOrNumber() || ch == QLatin1Char('_') || ch == QLatin1Char('.')
1141 || ch == QLatin1Char('{') || ch == QLatin1Char('}') || ch == QLatin1Char(',');
1142}
1143
1144/*!
1145 \a fileName is a master qdocconf file. It contains a list of
1146 qdocconf files and nothing else. Read the list and return it.
1147 */
1148QStringList Config::loadMaster(const QString &fileName)
1149{
1150 Location location;
1151 QFile fin(fileName);
1152 if (!fin.open(QFile::ReadOnly | QFile::Text)) {
1153 if (!Config::installDir.isEmpty()) {
1154 qsizetype prefix = location.filePath().size() - location.fileName().size();
1155 fin.setFileName(Config::installDir + QLatin1Char('/')
1156 + fileName.right(fileName.size() - prefix));
1157 }
1158 if (!fin.open(QFile::ReadOnly | QFile::Text))
1159 location.fatal(QStringLiteral("Cannot open master qdocconf file '%1': %2")
1160 .arg(fileName, fin.errorString()));
1161 }
1162 QTextStream stream(&fin);
1163 QStringList qdocFiles;
1164 QDir configDir(QFileInfo(fileName).canonicalPath());
1165 QString line = stream.readLine();
1166 while (!line.isNull()) {
1167 if (!line.isEmpty())
1168 qdocFiles.append(QFileInfo(configDir, line).filePath());
1169 line = stream.readLine();
1170 }
1171 fin.close();
1172 return qdocFiles;
1173}
1174
1175/*!
1176 Load, parse, and process a QDoc configuration file \a fileName.
1177 This function is only called by the other load() function, but
1178 is recursive, i.e., it calls itself when it sees an \c include
1179 statement in the configuration file.
1180
1181 The path of the loaded configuration file is pushed to a stack
1182 and set as the current working directory, unless \a fileName
1183 comes from the resource system. Configuration files from
1184 resources can only include() other files with an absolute path,
1185 or \c {:/path/to/qdocconf}.
1186 */
1187void Config::load(Location location, const QString &fileName)
1188{
1189 QFileInfo fileInfo(fileName);
1190 const bool isResource = fileName.startsWith(u':');
1191 if (!isResource)
1192 pushWorkingDir(fileInfo.canonicalPath());
1193 static const QRegularExpression keySyntax(QRegularExpression::anchoredPattern(QLatin1String("\\w+(?:\\.\\w+)*")));
1194
1195#define SKIP_CHAR()
1196 do {
1197 location.advance(c);
1198 ++i;
1199 c = text.at(i);
1200 cc = c.unicode();
1201 } while (0)
1202
1203#define SKIP_SPACES()
1204 while (c.isSpace() && cc != '\n')
1205 SKIP_CHAR()
1206
1207#define PUT_CHAR()
1208 word += c;
1209 SKIP_CHAR();
1210
1211 if (location.depth() > 16)
1212 location.fatal(QStringLiteral("Too many nested includes"));
1213
1214 QFile fin(isResource ? fileInfo.filePath() : fileInfo.fileName());
1215 if (!fin.open(QFile::ReadOnly | QFile::Text)) {
1216 if (!Config::installDir.isEmpty()) {
1217 qsizetype prefix = location.filePath().size() - location.fileName().size();
1218 fin.setFileName(Config::installDir + QLatin1Char('/')
1219 + fileName.right(fileName.size() - prefix));
1220 }
1221 if (!fin.open(QFile::ReadOnly | QFile::Text))
1222 location.fatal(
1223 QStringLiteral("Cannot open file '%1': %2").arg(fileName, fin.errorString()));
1224 }
1225
1226 QTextStream stream(&fin);
1227 QString text = stream.readAll();
1228 text += QLatin1String("\n\n");
1229 text += QLatin1Char('\0');
1230 fin.close();
1231
1232 location.push(fileName);
1233 location.start();
1234
1235 int i = 0;
1236 QChar c = text.at(0);
1237 uint cc = c.unicode();
1238 while (i < text.size()) {
1239 if (cc == 0) {
1240 ++i;
1241 } else if (c.isSpace()) {
1242 SKIP_CHAR();
1243 } else if (cc == '#') {
1244 do {
1245 SKIP_CHAR();
1246 } while (cc != '\n');
1247 } else if (isMetaKeyChar(c)) {
1248 Location keyLoc = location;
1249 bool plus = false;
1250 QStringList rhsValues;
1251 QList<ExpandVar> expandVars;
1252 QString word;
1253 bool inQuote = false;
1254 bool needsExpansion = false;
1255
1256 MetaStack stack;
1257 do {
1258 stack.process(c, location);
1259 SKIP_CHAR();
1260 } while (isMetaKeyChar(c));
1261
1262 const QStringList keys = stack.getExpanded(location);
1263 SKIP_SPACES();
1264
1265 if (keys.size() == 1 && keys.first() == QLatin1String("include")) {
1266 QString includeFile;
1267
1268 if (cc != '(')
1269 location.fatal(QStringLiteral("Bad include syntax"));
1270 SKIP_CHAR();
1271 SKIP_SPACES();
1272
1273 while (!c.isSpace() && cc != '#' && cc != ')') {
1274
1275 if (cc == '$') {
1276 QString var;
1277 SKIP_CHAR();
1278 while (c.isLetterOrNumber() || cc == '_') {
1279 var += c;
1280 SKIP_CHAR();
1281 }
1282 if (!var.isEmpty()) {
1283 const QByteArray val = qgetenv(var.toLatin1().data());
1284 if (val.isNull()) {
1285 location.fatal(QStringLiteral("Environment variable '%1' undefined")
1286 .arg(var));
1287 } else {
1288 includeFile += QString::fromLatin1(val);
1289 }
1290 }
1291 } else {
1292 includeFile += c;
1293 SKIP_CHAR();
1294 }
1295 }
1296 SKIP_SPACES();
1297 if (cc != ')')
1298 location.fatal(QStringLiteral("Bad include syntax"));
1299 SKIP_CHAR();
1300 SKIP_SPACES();
1301 if (cc != '#' && cc != '\n')
1302 location.fatal(QStringLiteral("Trailing garbage"));
1303
1304 /*
1305 Here is the recursive call.
1306 */
1307 if (isResource)
1308 load(location, includeFile);
1309 else
1310 load(location, QFileInfo(QDir(m_workingDirs.top()), includeFile).filePath());
1311 } else {
1312 /*
1313 It wasn't an include statement, so it's something else.
1314 We must see either '=' or '+=' next. If not, fatal error.
1315 */
1316 if (cc == '+') {
1317 plus = true;
1318 SKIP_CHAR();
1319 }
1320 if (cc != '=')
1321 location.fatal(QStringLiteral("Expected '=' or '+=' after key"));
1322 SKIP_CHAR();
1323 SKIP_SPACES();
1324
1325 for (;;) {
1326 if (cc == '\\') {
1327 qsizetype metaCharPos;
1328
1329 SKIP_CHAR();
1330 if (cc == '\n') {
1331 SKIP_CHAR();
1332 } else if (cc > '0' && cc < '8') {
1333 word += QChar(c.digitValue());
1334 SKIP_CHAR();
1335 } else if ((metaCharPos = QString::fromLatin1("abfnrtv").indexOf(c))
1336 != -1) {
1337 word += QLatin1Char("\a\b\f\n\r\t\v"[metaCharPos]);
1338 SKIP_CHAR();
1339 } else {
1340 PUT_CHAR();
1341 }
1342 } else if (c.isSpace() || cc == '#') {
1343 if (inQuote) {
1344 if (cc == '\n')
1345 location.fatal(QStringLiteral("Unterminated string"));
1346 PUT_CHAR();
1347 } else {
1348 if (!word.isEmpty() || needsExpansion) {
1349 rhsValues << word;
1350 word.clear();
1351 needsExpansion = false;
1352 }
1353 if (cc == '\n' || cc == '#')
1354 break;
1355 SKIP_SPACES();
1356 }
1357 } else if (cc == '"') {
1358 if (inQuote) {
1359 if (!word.isEmpty() || needsExpansion)
1360 rhsValues << word;
1361 word.clear();
1362 needsExpansion = false;
1363 }
1364 inQuote = !inQuote;
1365 SKIP_CHAR();
1366 } else if (cc == '$') {
1367 QString var;
1368 QChar delim(' ');
1369 bool braces = false;
1370 SKIP_CHAR();
1371 if (cc == '{') {
1372 SKIP_CHAR();
1373 braces = true;
1374 }
1375 while (c.isLetterOrNumber() || cc == '_') {
1376 var += c;
1377 SKIP_CHAR();
1378 }
1379 if (braces) {
1380 if (cc == ',') {
1381 SKIP_CHAR();
1382 delim = c;
1383 SKIP_CHAR();
1384 }
1385 if (cc == '}')
1386 SKIP_CHAR();
1387 else if (delim == '}')
1388 delim = QChar(); // null delimiter
1389 else
1390 location.fatal(QStringLiteral("Missing '}'"));
1391 }
1392 if (!var.isEmpty()) {
1393 const QByteArray val = qgetenv(var.toLatin1().constData());
1394 if (val.isNull()) {
1395 expandVars << ExpandVar(rhsValues.size(), word.size(), std::move(var), delim);
1396 needsExpansion = true;
1397 } else if (braces) { // ${VAR} inserts content from an env. variable for processing
1398 text.insert(i, QString::fromLatin1(val));
1399 c = text.at(i);
1400 cc = c.unicode();
1401 } else { // while $VAR simply reads the value and stores it to a config variable.
1402 word += QString::fromLatin1(val);
1403 }
1404 }
1405 } else {
1406 if (!inQuote && cc == '=')
1407 location.fatal(QStringLiteral("Unexpected '='"));
1408 PUT_CHAR();
1409 }
1410 }
1411 for (const auto &key : keys) {
1412 if (!keySyntax.match(key).hasMatch())
1413 keyLoc.fatal(QStringLiteral("Invalid key '%1'").arg(key));
1414
1415 ConfigVar configVar(key, rhsValues, QDir::currentPath(), keyLoc, expandVars);
1416 if (plus && m_configVars.contains(key)) {
1417 m_configVars[key].append(configVar);
1418 } else {
1419 m_configVars.insert(key, configVar);
1420 }
1421 }
1422 }
1423 } else {
1424 location.fatal(QStringLiteral("Unexpected character '%1' at beginning of line").arg(c));
1425 }
1426 }
1427 if (!isResource)
1429
1430#undef SKIP_CHAR
1431#undef SKIP_SPACES
1432#undef PUT_CHAR
1433}
1434
1435bool Config::isFileExcluded(const QString &fileName, const QSet<QString> &excludedFiles)
1436{
1437 for (const QString &entry : excludedFiles) {
1438 if (entry.contains(QLatin1Char('*')) || entry.contains(QLatin1Char('?'))) {
1439 QRegularExpression re(QRegularExpression::wildcardToRegularExpression(entry,
1440 QRegularExpression::UnanchoredWildcardConversion));
1441 if (re.match(fileName).hasMatch())
1442 return true;
1443 }
1444 }
1445 return excludedFiles.contains(fileName);
1446}
1447
1448QStringList Config::getFilesHere(const QString &uncleanDir, const QString &nameFilter,
1449 const Location &location, const QSet<QString> &excludedDirs,
1450 const QSet<QString> &excludedFiles)
1451{
1452 // TODO: Understand why location is used to branch the
1453 // canonicalization and why the two different methods are used.
1454 QString dir =
1455 location.isEmpty() ? QDir::cleanPath(uncleanDir) : QDir(uncleanDir).canonicalPath();
1456 QStringList result;
1457 if (excludedDirs.contains(dir))
1458 return result;
1459
1460 QDir dirInfo(dir);
1461
1462 dirInfo.setNameFilters(nameFilter.split(QLatin1Char(' ')));
1463 dirInfo.setSorting(QDir::Name);
1464 dirInfo.setFilter(QDir::Files);
1465 QStringList fileNames = dirInfo.entryList();
1466 for (const auto &file : std::as_const(fileNames)) {
1467 // TODO: Understand if this is needed and, should it be, if it
1468 // is indeed the only case that should be considered.
1469 if (!file.startsWith(QLatin1Char('~'))) {
1470 QString s = dirInfo.filePath(file);
1471 QString c = QDir::cleanPath(s);
1472 if (!isFileExcluded(c, excludedFiles))
1473 result.append(c);
1474 }
1475 }
1476
1477 dirInfo.setNameFilters(QStringList(QLatin1String("*")));
1478 dirInfo.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
1479 fileNames = dirInfo.entryList();
1480 for (const auto &file : fileNames)
1481 result += getFilesHere(dirInfo.filePath(file), nameFilter, location, excludedDirs,
1482 excludedFiles);
1483 return result;
1484}
1485
1486/*!
1487 Set \a dir as the working directory and push it onto the
1488 stack of working directories.
1489 */
1490void Config::pushWorkingDir(const QString &dir)
1491{
1492 m_workingDirs.push(dir);
1493 QDir::setCurrent(dir);
1494}
1495
1496/*!
1497 Pop the top entry from the stack of working directories.
1498 Set the working directory to the next one on the stack,
1499 if one exists.
1500 */
1502{
1503 Q_ASSERT(!m_workingDirs.isEmpty());
1504 m_workingDirs.pop();
1505 if (!m_workingDirs.isEmpty())
1506 QDir::setCurrent(m_workingDirs.top());
1507}
1508
1510 if (m_excludedPaths)
1511 return *m_excludedPaths;
1512
1513 const auto &excludedDirList = getCanonicalPathList(CONFIG_EXCLUDEDIRS);
1514 const auto &excludedFilesList = getCanonicalPathList(CONFIG_EXCLUDEFILES);
1515
1516 QSet<QString> excludedDirs = QSet<QString>(excludedDirList.cbegin(), excludedDirList.cend());
1517 QSet<QString> excludedFiles = QSet<QString>(excludedFilesList.cbegin(), excludedFilesList.cend());
1518
1519 m_excludedPaths.emplace(ExcludedPaths{std::move(excludedDirs), std::move(excludedFiles)});
1520
1521 return *m_excludedPaths;
1522}
1523
1524/*!
1525 Returns the set of file patterns that identify internal implementation files.
1526 Classes declared in files matching these patterns will have their nodes marked
1527 as internal, excluding them and their members from documentation requirements
1528 when showInternal is false.
1529
1530 \sa isFileExcluded()
1531*/
1533{
1534 const QStringList patterns = get(CONFIG_INTERNALFILEPATTERNS).asStringList();
1535 return QSet<QString>(patterns.cbegin(), patterns.cend());
1536}
1537
1538/*!
1539 Returns a reference to the pre-compiled internal file patterns structure.
1540 This method caches the compiled patterns for efficient reuse across multiple
1541 file checks. The patterns are compiled once when first accessed and remain
1542 valid until the configuration is cleared or reloaded.
1543
1544 The returned structure separates patterns into:
1545 - exactMatches: Patterns without wildcards for direct string matching.
1546 - regexPatterns: Pre-compiled regular expressions for regular expression
1547 patterns.
1548
1549 \sa matchesInternalFilePattern(), clear()
1550*/
1552{
1553 if (m_internalFilePatterns)
1554 return *m_internalFilePatterns;
1555
1556 InternalFilePatterns compiled;
1557 const QStringList patterns = get(CONFIG_INTERNALFILEPATTERNS).asStringList();
1558
1559 for (const QString &pattern : patterns) {
1560 static const QRegularExpression regexMetaChars(
1561 uR"(\.\*|\.\+|\.\{|\\‍[\.\*\?]|[\^\$\‍[\‍]{}()|+])"_s);
1562 bool isRawRegex = regexMetaChars.match(pattern).hasMatch();
1563
1564 if (isRawRegex) {
1565 QRegularExpression re(pattern);
1566 if (!re.isValid()) {
1567 qWarning() << "Invalid regex pattern in internalfilepatterns:" << pattern
1568 << "-" << re.errorString();
1569 continue;
1570 }
1571 re.optimize();
1572 compiled.regexPatterns.append(re);
1573 } else if (pattern.contains(u'*') || pattern.contains(u'?')) {
1574 QString regexPattern = QRegularExpression::wildcardToRegularExpression(pattern);
1575 QRegularExpression re(regexPattern);
1576 re.optimize();
1577 compiled.globPatterns.append(re);
1578 } else {
1579 compiled.exactMatches.insert(pattern);
1580 }
1581 }
1582
1583 m_internalFilePatterns.emplace(std::move(compiled));
1584 return *m_internalFilePatterns;
1585}
1586
1587/*!
1588 Returns true if the given \a filePath matches any pattern in the
1589 \a patterns structure. This is an optimized version that uses pre-compiled
1590 regular expressions.
1591
1592 The function first checks for exact matches (patterns without wildcards),
1593 which compare against the full normalized path. Then it checks glob patterns
1594 against the filename only, and finally regex patterns against the full path.
1595
1596 Path normalization is handled internally: all backslashes are converted to
1597 forward slashes to ensure cross-platform consistency, as patterns are
1598 expected to use forward slashes.
1599
1600 \sa getInternalFilePatternsCompiled()
1601*/
1602bool Config::matchesInternalFilePattern(const QString &filePath,
1603 const InternalFilePatterns &patterns) noexcept
1604{
1605 if (patterns.exactMatches.isEmpty() && patterns.globPatterns.isEmpty()
1606 && patterns.regexPatterns.isEmpty())
1607 return false;
1608
1609 QString norm = filePath;
1610 norm.replace(QChar('\\'), QChar('/'));
1611
1612 if (patterns.exactMatches.contains(norm))
1613 return true;
1614
1615 const QString fileName = QFileInfo(norm).fileName();
1616 for (const QRegularExpression &re : patterns.globPatterns) {
1617 if (re.match(fileName).hasMatch())
1618 return true;
1619 }
1620
1621 for (const QRegularExpression &re : patterns.regexPatterns) {
1622 if (re.match(norm).hasMatch())
1623 return true;
1624 }
1625
1626 return false;
1627}
1628
1629/*!
1630 Returns a SourceLink struct with settings required to
1631 construct source links to API entities.
1632*/
1634{
1635 if (m_sourceLink)
1636 return *m_sourceLink;
1637
1638 const auto srcUrl{CONFIG_URL + Config::dot + CONFIG_SOURCES};
1639
1640 const auto baseUrl = m_configVars.value(srcUrl).asString();
1641 const auto rootPath = m_configVars.value(srcUrl + dot + CONFIG_ROOTDIR).asString();
1642 const auto linkText = m_configVars.value(srcUrl + dot + "linktext").asString();
1643 const auto enabled = m_configVars.value(srcUrl + dot + "enabled").asBool();
1644
1645 m_sourceLink.emplace(SourceLink{baseUrl, rootPath, linkText, enabled});
1646 return *m_sourceLink;
1647}
1648
1650 static QStringList accepted_header_file_extensions{
1651 "ch", "h", "h++", "hh", "hpp", "hxx"
1652 };
1653
1654 const auto& [excludedDirs, excludedFiles] = getExcludedPaths();
1655
1656 QStringList headerList =
1657 getAllFiles(CONFIG_HEADERS, CONFIG_HEADERDIRS, excludedDirs, excludedFiles);
1658
1659 std::set<HeaderFilePath> headers{};
1660
1661 for (const auto& header : headerList) {
1662 if (header.contains("doc/snippets")) continue;
1663
1664 if (!accepted_header_file_extensions.contains(QFileInfo{header}.suffix()))
1665 continue;
1666
1667 headers.insert(HeaderFilePath{QFileInfo{header}.canonicalPath(), QFileInfo{header}.fileName()});
1668 }
1669
1670 return headers;
1671}
1672
1674{
1675 return m_configVars.value(CONFIG_SHOWAUTOGENERATEDDOCS).asBool();
1676}
1677
1678InclusionPolicy Config::createInclusionPolicy() const
1679{
1680 InclusionPolicy policy;
1681
1682 policy.includePrivateFunction = includePrivateFunction();
1683 policy.includePrivateType = includePrivateType();
1684 policy.includePrivateVariable = includePrivateVariable();
1685 policy.showInternal = showInternal();
1686 policy.showAutoGeneratedDocs = showAutoGeneratedDocs();
1687
1688 return policy;
1689}
1690
1691QT_END_NAMESPACE
contains all the information for a single config variable in a .qdocconf file.
Definition config.h:43
QString asString(const QString defaultString=QString()) const
Returns this configuration variable as a string.
Definition config.cpp:280
QStringList asStringList() const
Returns this config variable as a string list.
Definition config.cpp:297
int asInt() const
Returns this configuration variable as an integer; iterates through the string list,...
Definition config.cpp:330
bool asBool() const
Returns this config variable as a boolean.
Definition config.cpp:317
QSet< QString > asStringSet() const
Returns this config variable as a string set.
Definition config.cpp:308
The Config class contains the configuration variables for controlling how qdoc produces documentation...
Definition config.h:95
static QString installDir
Definition config.h:185
QString getOutputDir(const QString &format=QString("HTML")) const
Function to return the correct outputdir for the output format.
Definition config.cpp:620
std::set< HeaderFilePath > getHeaderFiles()
Definition config.cpp:1649
static bool generateExamples
Definition config.h:183
void insertStringList(const QString &var, const QStringList &values)
Adds the values from a string list to the configuration variable var.
Definition config.cpp:541
static const QString dot
Definition config.h:181
QStringList getAllFiles(const QString &filesVar, const QString &dirsVar, const QSet< QString > &excludedDirs=QSet< QString >(), const QSet< QString > &excludedFiles=QSet< QString >())
Builds and returns a list of file pathnames for the file type specified by filesVar (e....
Definition config.cpp:853
QSet< QString > getInternalFilePatterns() const
Returns the set of file patterns that identify internal implementation files.
Definition config.cpp:1532
static bool parseCppComments
Definition config.h:184
void reset()
Resets the Config instance and loads the default values.
Definition config.cpp:419
InclusionPolicy createInclusionPolicy() const
Definition config.cpp:1678
void setStringList(const QString &var, const QStringList &values)
Sets the values of a configuration variable var from a string list.
Definition config.cpp:532
void clear()
Clears the location and internal maps for config variables.
Definition config.cpp:405
QSet< QString > subVars(const QString &var) const
This function is slower than it could be.
Definition config.cpp:794
~Config()
Definition config.cpp:397
bool includePrivateType() const
Definition config.h:501
PathFlags
Flags used for retrieving canonicalized paths from Config.
Definition config.h:101
bool includePrivateVariable() const
Definition config.h:507
const ExcludedPaths & getExcludedPaths()
Definition config.cpp:1509
QSet< QString > getOutputFormats() const
Function to return the correct outputformats.
Definition config.cpp:667
static void popWorkingDir()
Pop the top entry from the stack of working directories.
Definition config.cpp:1501
bool showAutoGeneratedDocs() const
Definition config.cpp:1673
@ Generate
Definition config.h:99
@ Prepare
Definition config.h:99
QRegularExpression getRegExp(const QString &var) const
Calls getRegExpList() with the control variable var and iterates through the resulting list of regula...
Definition config.cpp:757
QString getExampleProjectFile(const QString &examplePath)
Returns the path to the project file for examplePath, or an empty string if no project file was found...
Definition config.cpp:902
bool includePrivateFunction() const
Definition config.h:495
static QSet< QString > overrideOutputFormats
Definition config.h:187
static QString overrideOutputDir
Definition config.h:186
QStringList getCanonicalPathList(const QString &var, PathFlags flags=None) const
Returns a path list where all paths from the config variable var are canonicalized.
Definition config.cpp:696
void load(const QString &fileName)
Loads and parses the qdoc configuration file fileName.
Definition config.cpp:453
const SourceLink & getSourceLink()
Returns a SourceLink struct with settings required to construct source links to API entities.
Definition config.cpp:1633
bool showInternal() const
Definition config.h:121
QList< QRegularExpression > getRegExpList(const QString &var) const
Looks up the configuration variable var in the string list map, converts the string list to a list of...
Definition config.cpp:779
QStringList getExampleImageFiles(const QSet< QString > &excludedDirs, const QSet< QString > &excludedFiles)
Definition config.cpp:879
QStringList getExampleQdocFiles(const QSet< QString > &excludedDirs, const QSet< QString > &excludedFiles)
Definition config.cpp:867
const InternalFilePatterns & getInternalFilePatternsCompiled()
Returns a reference to the pre-compiled internal file patterns structure.
Definition config.cpp:1551
void init(const QString &programName, const QStringList &args)
Initializes the Config with programName and sets all internal state variables to either default value...
Definition config.cpp:390
QString getIncludeFilePath(const QString &fileName) const
Searches for a path to fileName in 'sources', 'sourcedirs', and 'exampledirs' config variables and re...
Definition config.cpp:815
The Location class provides a way to mark a location in a file.
Definition location.h:20
int depth() const
Definition location.h:46
void start()
If the file position on top of the stack has a line number less than 1, set its line number to 1 and ...
Definition location.cpp:136
An entry in a stack, where each entry is a list of string values.
Definition config.cpp:154
QStringList next
Definition config.cpp:160
void close()
Stop accumulating values and append the list of accumulated values to the complete list of accumulate...
Definition config.cpp:178
QStringList accum
Definition config.cpp:159
void open()
Start accumulating values in a list by appending an empty string to the list.
Definition config.cpp:168
This class maintains a stack of values of config file variables.
Definition config.cpp:190
QStringList getExpanded(const Location &location)
Returns the accumulated string values.
Definition config.cpp:243
void process(QChar ch, const Location &location)
Processes the character ch using the location.
Definition config.cpp:213
MetaStack()
The default constructor pushes a new stack entry and opens it.
Definition config.cpp:202
Q_DECLARE_TYPEINFO(MetaStackEntry, Q_RELOCATABLE_TYPE)
#define SKIP_CHAR()
#define PUT_CHAR()
#define SKIP_SPACES()
#define CONFIG_SOURCES
Definition config.h:458
#define CONFIG_REDIRECTDOCUMENTATIONTODEVNULL
Definition config.h:447
#define CONFIG_FILEEXTENSIONS
Definition config.h:472
#define CONFIG_OUTPUTDIR
Definition config.h:436
#define CONFIG_AUTOLINKERRORS
Definition config.h:384
#define CONFIG_SHOWINTERNAL
Definition config.h:454
#define CONFIG_SHOWAUTOGENERATEDDOCS
Definition config.h:453
#define CONFIG_DOCBOOKEXTENSIONS
Definition config.h:395
#define CONFIG_SINGLEEXEC
Definition config.h:455
#define CONFIG_EXAMPLES
Definition config.h:399
#define CONFIG_EXAMPLEDIRS
Definition config.h:398
#define CONFIG_URL
Definition config.h:468
#define CONFIG_SYNTAXHIGHLIGHTING
Definition config.h:461
#define CONFIG_DEFINES
Definition config.h:392
#define CONFIG_TIMESTAMPS
Definition config.h:465
#define CONFIG_NOLINKERRORS
Definition config.h:435
#define CONFIG_LOGPROGRESS
Definition config.h:427
#define CONFIG_PROJECT
Definition config.h:444
#define CONFIG_PARSECPPCOMMENTS
Definition config.h:440
#define CONFIG_SOURCEDIRS
Definition config.h:456
#define CONFIG_INTERNALFILEPATTERNS
Definition config.h:420
#define CONFIG_ROOTDIR
Definition config.h:451
#define CONFIG_HEADERS
Definition config.h:407
#define CONFIG_OUTPUTFORMATS
Definition config.h:437
#define CONFIG_IMAGEEXTENSIONS
Definition config.h:473
#define CONFIG_EXCLUDEFILES
Definition config.h:402
#define CONFIG_MODULEHEADER
Definition config.h:432
#define CONFIG_EXCLUDEDIRS
Definition config.h:401
#define CONFIG_HEADERDIRS
Definition config.h:406
#define CONFIG_REPORTMISSINGALTTEXTFORIMAGES
Definition config.h:448
#define CONFIG_INCLUDEPATHS
Definition config.h:418
Combined button and popup list for selecting options.
#define SET(x, y)
static QString HEADERSTYLES
Definition config.h:311
static QString IGNORESINCE
Definition config.h:316
static QString CODESUFFIX
Definition config.h:291
static QString EXAMPLEDIRS
Definition config.h:300
static QString CODEINDENT
Definition config.h:288
static QString CODELANGUAGES
Definition config.h:289
static QString HEADERDIRS
Definition config.h:308
static QString ROOTDIR
Definition config.h:354
static QString HEADERS
Definition config.h:309
static QString NATURALLANGUAGE
Definition config.h:336
static QString WARNABOUTMISSINGPROJECTFILES
Definition config.h:380
static QString SINGLEEXEC
Definition config.h:358
static QString LANGUAGE
Definition config.h:328
static QString SOURCEDIRS
Definition config.h:359
static QString IGNOREDIRECTIVES
Definition config.h:314
static QString SHOWAUTOGENERATEDDOCS
Definition config.h:356
static QString EXCLUDEDIRS
Definition config.h:303
static QString INCLUSIVE
Definition config.h:324
static QString FILEEXTENSIONS
Definition config.h:375
static QString AUTOLINKERRORS
Definition config.h:286
static QString LOGWARNINGSDISABLECLIARGS
Definition config.h:332
static QString OUTPUTFORMATS
Definition config.h:340
static QString REDIRECTDOCUMENTATIONTODEVNULL
Definition config.h:350
static QString LANDINGPAGE
Definition config.h:326
static QString TEMPLATEPACKAGE
\variable ConfigStrings::TEMPLATEPACKAGE
Definition config.h:367
static QString SCRIPTS
Definition config.h:355
static QString QHP
Definition config.h:352
static QString MODULEHEADER
Definition config.h:335
static QString DEPENDS
Definition config.h:295
static QString VERSION
Definition config.h:373
static QString CPPCLASSESPAGE
Definition config.h:292
static QString MACRO
Definition config.h:333
static QString PRODUCTNAME
Definition config.h:346
static QString SOURCES
Definition config.h:361
static QString BUILDVERSION
Definition config.h:287
static QString DOCBOOKEXTENSIONS
Definition config.h:297
static QString VERSIONSYM
Definition config.h:374
static QString IMAGEEXTENSIONS
Definition config.h:376
static QString ENDHEADER
Definition config.h:299
static QString PUBLICATIONLAYOUT
\variable ConfigStrings::PUBLICATIONLAYOUT
Definition config.h:349
static QString EXAMPLESINSTALLPATH
Definition config.h:302
static QString DEFINES
Definition config.h:294
static QString INCLUDEPRIVATE
Definition config.h:322
static QString HEADERSCRIPTS
Definition config.h:310
static QString SOURCEENCODING
Definition config.h:360
static QString IGNORETOKENS
Definition config.h:315
static QString TAGFILE
Definition config.h:366
static QString IMAGEDIRS
Definition config.h:318
static QString NAVIGATION
Definition config.h:337
static QString SYNTAXHIGHLIGHTING
Definition config.h:364
static QString IGNOREWORDS
Definition config.h:317
static QString EXAMPLES
Definition config.h:301
static QString INCLUDEPATHS
Definition config.h:321
static QString WARNINGLIMIT
Definition config.h:381
static QString STYLESHEETS
Definition config.h:363
static QString LANDINGTITLE
Definition config.h:327
static QString PIPELINE
\variable ConfigStrings::PIPELINE
Definition config.h:344
static QString USEALTTEXTASTITLE
Definition config.h:372
static QString CODEPREFIX
Definition config.h:290
static QString LOGPROGRESS
Definition config.h:330
static QString FALSEHOODS
Definition config.h:306
static QString PARSECPPCOMMENTS
Definition config.h:343
static QString HOMEPAGE
Definition config.h:312
static QString IMAGESOUTPUTDIR
Definition config.h:319
static QString TRADEMARKSPAGE
Definition config.h:370
static QString QMLTYPESPAGE
Definition config.h:377
static QString SPURIOUS
Definition config.h:362
static QString LOGWARNINGS
Definition config.h:331
static QString INDEXES
Definition config.h:325
static QString OUTPUTDIR
Definition config.h:339
static QString EXCLUDEFILES
Definition config.h:304
static QString EXTRAIMAGES
Definition config.h:305
static QString PRELIMINARY
Definition config.h:345
static QString TOCTITLES
Definition config.h:369
static QString NOLINKERRORS
Definition config.h:338
static QString LOCATIONINFO
Definition config.h:329
static QString TABSIZE
Definition config.h:365
static QString TIMESTAMPS
Definition config.h:368
static QString WARNABOUTMISSINGIMAGES
Definition config.h:379
static QString HOMETITLE
Definition config.h:313
static QString DOCUMENTATIONINHEADERS
Definition config.h:298
static QString SHOWINTERNAL
Definition config.h:357
static QString CPPCLASSESTITLE
Definition config.h:293
static QString QUOTINGINFORMATION
Definition config.h:353
static QString FORMATTING
Definition config.h:307
static QString PROJECT
Definition config.h:347
static QString MANIFESTMETA
Definition config.h:334
static QString OUTPUTPREFIXES
Definition config.h:341
static QString DESCRIPTION
Definition config.h:296
static QString REPORTMISSINGALTTEXTFORIMAGES
Definition config.h:351
static QString INTERNALFILEPATTERNS
Definition config.h:323
static QString OUTPUTSUFFIXES
Definition config.h:342
static QString URL
Definition config.h:371
static QString PROJECTROOT
Definition config.h:348
static QString QMLTYPESTITLE
Definition config.h:378