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