diff options
-rw-r--r-- | src/qdoc/clangcodeparser.cpp | 36 | ||||
-rw-r--r-- | src/qdoc/config.cpp | 77 | ||||
-rw-r--r-- | src/qdoc/config.h | 9 | ||||
-rw-r--r-- | src/qdoc/doc/qdoc-manual-qdocconf.qdoc | 70 | ||||
-rw-r--r-- | src/qdoc/generator.cpp | 2 | ||||
-rw-r--r-- | src/qdoc/location.h | 2 | ||||
-rw-r--r-- | tests/auto/qdoc/config/testdata/configs/includepaths.qdocconf | 2 | ||||
-rw-r--r-- | tests/auto/qdoc/config/testdata/includepaths/include/framework/ignore.h | 1 | ||||
-rw-r--r-- | tests/auto/qdoc/config/testdata/includepaths/include/more/ignore.h | 1 | ||||
-rw-r--r-- | tests/auto/qdoc/config/testdata/includepaths/include/purpose.h | 1 | ||||
-rw-r--r-- | tests/auto/qdoc/config/testdata/includepaths/include/system/ignore.h | 1 | ||||
-rw-r--r-- | tests/auto/qdoc/config/testdata/includepaths/includepaths.qdocconf | 16 | ||||
-rw-r--r-- | tests/auto/qdoc/config/tst_config.cpp | 23 |
13 files changed, 197 insertions, 44 deletions
diff --git a/src/qdoc/clangcodeparser.cpp b/src/qdoc/clangcodeparser.cpp index b8feca108..d61b24270 100644 --- a/src/qdoc/clangcodeparser.cpp +++ b/src/qdoc/clangcodeparser.cpp @@ -1096,38 +1096,18 @@ void ClangCodeParser::initializeParser() { Config &config = Config::instance(); m_version = config.getString(CONFIG_VERSION); - auto args = config.getStringList(CONFIG_INCLUDEPATHS); - QSet<QString> seen; - m_includePaths.clear(); - + auto args = config.getCanonicalPathList(CONFIG_INCLUDEPATHS, + Config::IncludePaths); #ifdef Q_OS_MACOS args.append(Utilities::getInternalIncludePaths(QStringLiteral("clang++"))); #endif - - // Remove empty paths and duplicates and add -I and canonicalize if necessary - for (const auto &p : args) { - QByteArray option; - QString rawpath; - if (p.startsWith(QLatin1String("-I")) || p.startsWith(QLatin1String("-F"))) { - rawpath = p.mid(2).trimmed(); - option = p.left(2).toUtf8(); - } else if (p.startsWith(QLatin1String("-isystem"))) { - rawpath = p.mid(8).trimmed(); - option = "-isystem"; - } else { - rawpath = p; - option = "-I"; - } - if (rawpath.isEmpty() || seen.contains(rawpath)) - continue; - seen.insert(rawpath); - QByteArray path(rawpath.toUtf8()); - QFileInfo fi(QDir::current(), rawpath); - if (fi.exists()) - path = fi.canonicalFilePath().toUtf8(); - path.prepend(option); - m_includePaths.append(path); + m_includePaths.clear(); + for (const auto &path : qAsConst(args)) { + if (!path.isEmpty()) + m_includePaths.append(path.toUtf8()); } + m_includePaths.erase(std::unique(m_includePaths.begin(), m_includePaths.end()), + m_includePaths.end()); CppCodeParser::initializeParser(); m_pchFileDir.reset(nullptr); m_allHeaders.clear(); diff --git a/src/qdoc/config.cpp b/src/qdoc/config.cpp index c2beda80d..41dc9b2c6 100644 --- a/src/qdoc/config.cpp +++ b/src/qdoc/config.cpp @@ -235,10 +235,29 @@ QMap<QString, QStringList> Config::m_includeFilesMap; \brief The Config class contains the configuration variables for controlling how qdoc produces documentation. - Its load() function, reads, parses, and processes a qdocconf file. + Its load() function reads, parses, and processes a qdocconf file. */ /*! + \enum Config::PathFlags + + Flags used for retrieving canonicalized paths from Config. + + \value Validate + Issue a warning for paths that do not exist and + remove them from the returned list. + + \value IncludePaths + Assume the variable contains include paths with + prefixes such as \c{-I} that are to be removed + before canonicalizing and then re-inserted. + + \omitvalue None + + \sa getCanonicalPathList() +*/ + +/*! Initializes the Config with \a programName and sets all internal state variables to either default values or to ones defined in command line arguments \a args. @@ -573,12 +592,16 @@ QStringList Config::getStringList(const QString &var) const } /*! - Returns the a path list where all paths from the config variable \a var - are canonicalized. If \a validate is true, outputs a warning for invalid - paths. If \a var is defined, updates the internal location to the - location of \a var for the purposes of error reporting. + Returns a path list where all paths from the config variable \a var + are canonicalized. If \a flags contains \c Validate, outputs a warning + for invalid paths. The \c IncludePaths flag is used as a hint to strip + away potential prefixes found in include paths before attempting to + canonicalize. + + \note The internal location is updated to the location of \a var for + the purposes of error reporting. */ -QStringList Config::getCanonicalPathList(const QString &var, bool validate) const +QStringList Config::getCanonicalPathList(const QString &var, PathFlags flags) const { QStringList result; const auto &configVar = m_configVars.value(var); @@ -586,19 +609,47 @@ QStringList Config::getCanonicalPathList(const QString &var, bool validate) cons for (const auto &value : configVar.m_values) { const QString ¤tPath = value.m_path; - QDir dir(value.m_value.simplified()); + QString rawValue = value.m_value.simplified(); + QString prefix; + + if (flags & IncludePaths) { + const QStringList prefixes = QStringList() + << QLatin1String("-I") + << QLatin1String("-F") + << QLatin1String("-isystem"); + const auto end = std::end(prefixes); + const auto it = + std::find_if(std::begin(prefixes), end, + [&rawValue](const QString &p) { + return rawValue.startsWith(p); + }); + if (it != end) { + prefix = *it; + rawValue.remove(0, it->size()); + if (rawValue.isEmpty()) + continue; + } else { + prefix = prefixes[0]; // -I as default + } + } + + QDir dir(rawValue.trimmed()); const QString path = dir.path(); if (dir.isRelative()) dir.setPath(currentPath + QLatin1Char('/') + path); - if (validate && !QFileInfo::exists(dir.path())) + if ((flags & Validate) && !QFileInfo::exists(dir.path())) m_lastLocation.warning(QStringLiteral("Cannot find file or directory: %1").arg(path)); else { const QString canonicalPath = dir.canonicalPath(); if (!canonicalPath.isEmpty()) - result.append(canonicalPath); + result.append(prefix + canonicalPath); else if (path.contains(QLatin1Char('*')) || path.contains(QLatin1Char('?'))) result.append(path); + else + qCDebug(lcQdoc) << + qUtf8Printable(QStringLiteral("%1: Ignored nonexistent path \'%2\'") + .arg(m_lastLocation.toString()).arg(rawValue)); } } return result; @@ -607,8 +658,8 @@ QStringList Config::getCanonicalPathList(const QString &var, bool validate) cons /*! Calls getRegExpList() with the control variable \a var and iterates through the resulting list of regular expressions, - concatening them with some extras characters to form a single - QRegularExpression, which is returned/ + concatenating them with extra characters to form a single + QRegularExpression, which is then returned. \sa getRegExpList() */ @@ -712,8 +763,8 @@ QStringList Config::getAllFiles(const QString &filesVar, const QString &dirsVar, const QSet<QString> &excludedDirs, const QSet<QString> &excludedFiles) { - QStringList result = getCanonicalPathList(filesVar, true); - const QStringList dirs = getCanonicalPathList(dirsVar, true); + QStringList result = getCanonicalPathList(filesVar, Validate); + const QStringList dirs = getCanonicalPathList(dirsVar, Validate); const QString nameFilter = getString(filesVar + dot + CONFIG_FILEEXTENSIONS); diff --git a/src/qdoc/config.h b/src/qdoc/config.h index 00c741d9e..e3b8bf9d8 100644 --- a/src/qdoc/config.h +++ b/src/qdoc/config.h @@ -118,6 +118,12 @@ public: enum QDocPass { Neither, Prepare, Generate }; + enum PathFlags : unsigned char { + None = 0x0, + Validate = 0x1, + IncludePaths = 0x2 + }; + void init(const QString &programName, const QStringList &args); [[nodiscard]] bool getDebug() const { return m_debug; } [[nodiscard]] bool showInternal() const { return m_showInternal; } @@ -142,7 +148,8 @@ public: const QString &defaultString = QString()) const; [[nodiscard]] QSet<QString> getStringSet(const QString &var) const; [[nodiscard]] QStringList getStringList(const QString &var) const; - [[nodiscard]] QStringList getCanonicalPathList(const QString &var, bool validate = false) const; + [[nodiscard]] QStringList getCanonicalPathList(const QString &var, + PathFlags flags = None) const; [[nodiscard]] QRegularExpression getRegExp(const QString &var) const; [[nodiscard]] QList<QRegularExpression> getRegExpList(const QString &var) const; [[nodiscard]] QSet<QString> subVars(const QString &var) const; diff --git a/src/qdoc/doc/qdoc-manual-qdocconf.qdoc b/src/qdoc/doc/qdoc-manual-qdocconf.qdoc index b1bda2955..c47d49784 100644 --- a/src/qdoc/doc/qdoc-manual-qdocconf.qdoc +++ b/src/qdoc/doc/qdoc-manual-qdocconf.qdoc @@ -182,6 +182,7 @@ \li \l {HTML.footer-variable} {HTML.footer} \li \l {HTML.postheader-variable} {HTML.postheader} \li \l {HTML.style-variable} {HTML.style} + \li \l {includepaths-variable} {includepaths} \li \l {ignorewords-variable} {ignorewords} \li \l {ignoresince-variable} {ignoresince} \li \l {imagedirs-variable} {imagedirs} @@ -192,6 +193,7 @@ \li \l {locationinfo-variable} {locationinfo} \li \l {macro-variable} {macro} \li \l {manifestmeta-variable} {manifestmeta} + \li \l {moduleheader-variable} {moduleheader} \li \l {navigation-variable} {navigation} \li \l {outputdir-variable} {outputdir} \li \l {outputformats-variable} {outputformats} @@ -697,6 +699,28 @@ See also \l headerdirs. + \target includepaths-variable + \section1 includepaths + + The \c includepaths variable is used for passing additional + include paths to the Clang parser that QDoc uses for parsing C++ + code for documentation comments. + + The variable accepts a list of paths, prefixed with \c{-I} (include + path), \c {-F} (\macos framework include path), or \c{-isystem} + (system include path). If a prefix is omitted, \c{-I} is used by + default. + + Paths relative to the current .qdocconf file are resolved into + absolute paths. Paths that do not exist in the file system are + ignored. + + \note For Qt documentation projects, the build system typically + provides the required include paths as command line + arguments when invoking QDoc. + + See also \l moduleheader. + \target ignorewords-variable \section1 ignorewords @@ -982,6 +1006,52 @@ See the \l{Manifest Meta Content} section for more information. + \target moduleheader-variable + \section1 moduleheader + + The \c moduleheader variable defines the name of the module + header of a documented C++ module. + + Projects that document C++ APIs require a module-level header + that includes all public classes, namespaces and header files + for the module. The Clang parser in QDoc uses this file to + build a pre-compiled header (PCH) for the module to increase + the speed of parsing source files. + + By default, the \l{project-variable}{project} name is used + also as the module header name. + + \badcode + project = QtCore + \endcode + + With the above project name, QDoc searches a module header + \e QtCore in all known include paths; first using the paths + passed as command line arguments, then the paths listed in + the \l includepaths variable. + + QDoc will issue a warning if the module header is not found. + It will then attempt to build an artificial module header + based on the headers listed in the \l {headerdirs-variable} + {headerdirs} variable. + + For Qt documentation projects, the build system typically + provides QDoc with correct include paths to locate the + module header, provided that the \c project variable is set + correctly. The \c moduleheader variable provides an + alternative file name for QDoc to search for. + + If the project contains no C++ documentation, QDoc should be + instructed to skip generating a PCH by setting \c moduleheader + to an empty string: + + \badcode + # No C++ code to document in this project + moduleheader = + \endcode + + See also \l includepaths and \l project. + \target naturallanguage-variable \section1 naturallanguage diff --git a/src/qdoc/generator.cpp b/src/qdoc/generator.cpp index 50904cb49..5f65236f2 100644 --- a/src/qdoc/generator.cpp +++ b/src/qdoc/generator.cpp @@ -1715,7 +1715,7 @@ void Generator::initialize() void Generator::copyTemplateFiles(const QString &configVar, const QString &subDir) { Config &config = Config::instance(); - QStringList files = config.getCanonicalPathList(configVar, true); + QStringList files = config.getCanonicalPathList(configVar, Config::Validate); if (!files.isEmpty()) { QDir dirInfo; QString templateDir = s_outDir + QLatin1Char('/') + subDir; diff --git a/src/qdoc/location.h b/src/qdoc/location.h index dbf603394..bb1b6f01f 100644 --- a/src/qdoc/location.h +++ b/src/qdoc/location.h @@ -68,6 +68,7 @@ public: [[nodiscard]] int lineNo() const { return m_stkTop->m_lineNo; } [[nodiscard]] int columnNo() const { return m_stkTop->m_columnNo; } [[nodiscard]] bool etc() const { return m_etc; } + [[nodiscard]] QString toString() const; void warning(const QString &message, const QString &details = QString()) const; void error(const QString &message, const QString &details = QString()) const; void fatal(const QString &message, const QString &details = QString()) const; @@ -92,7 +93,6 @@ private: friend class QTypeInfo<StackEntry>; void emitMessage(MessageType type, const QString &message, const QString &details) const; - [[nodiscard]] QString toString() const; [[nodiscard]] QString top() const; private: diff --git a/tests/auto/qdoc/config/testdata/configs/includepaths.qdocconf b/tests/auto/qdoc/config/testdata/configs/includepaths.qdocconf new file mode 100644 index 000000000..2d6ff22af --- /dev/null +++ b/tests/auto/qdoc/config/testdata/configs/includepaths.qdocconf @@ -0,0 +1,2 @@ +project = IncludePaths +include(../includepaths/includepaths.qdocconf) diff --git a/tests/auto/qdoc/config/testdata/includepaths/include/framework/ignore.h b/tests/auto/qdoc/config/testdata/includepaths/include/framework/ignore.h new file mode 100644 index 000000000..b2a4ba591 --- /dev/null +++ b/tests/auto/qdoc/config/testdata/includepaths/include/framework/ignore.h @@ -0,0 +1 @@ +# nothing here diff --git a/tests/auto/qdoc/config/testdata/includepaths/include/more/ignore.h b/tests/auto/qdoc/config/testdata/includepaths/include/more/ignore.h new file mode 100644 index 000000000..b2a4ba591 --- /dev/null +++ b/tests/auto/qdoc/config/testdata/includepaths/include/more/ignore.h @@ -0,0 +1 @@ +# nothing here diff --git a/tests/auto/qdoc/config/testdata/includepaths/include/purpose.h b/tests/auto/qdoc/config/testdata/includepaths/include/purpose.h new file mode 100644 index 000000000..0f7af352b --- /dev/null +++ b/tests/auto/qdoc/config/testdata/includepaths/include/purpose.h @@ -0,0 +1 @@ +#define PURPOSE "Pass butter" diff --git a/tests/auto/qdoc/config/testdata/includepaths/include/system/ignore.h b/tests/auto/qdoc/config/testdata/includepaths/include/system/ignore.h new file mode 100644 index 000000000..b2a4ba591 --- /dev/null +++ b/tests/auto/qdoc/config/testdata/includepaths/include/system/ignore.h @@ -0,0 +1 @@ +# nothing here diff --git a/tests/auto/qdoc/config/testdata/includepaths/includepaths.qdocconf b/tests/auto/qdoc/config/testdata/includepaths/includepaths.qdocconf new file mode 100644 index 000000000..6288c4258 --- /dev/null +++ b/tests/auto/qdoc/config/testdata/includepaths/includepaths.qdocconf @@ -0,0 +1,16 @@ +includepaths = -I./include + +# without prefix but same path, should be identical +# (Config should not remove duplicates) +includepaths += include + +# space between prefix and path - incorrect but we allow it +includepaths += -I include/more + +# system paths and framework paths +includepaths += \ + -F./include/framework \ + -isysteminclude/system + +# nonexistent paths are to be ignored +includepaths += invalid diff --git a/tests/auto/qdoc/config/tst_config.cpp b/tests/auto/qdoc/config/tst_config.cpp index 58d8eae15..dddfd0878 100644 --- a/tests/auto/qdoc/config/tst_config.cpp +++ b/tests/auto/qdoc/config/tst_config.cpp @@ -43,6 +43,7 @@ private slots: void includePathsFromCommandLine(); void variables(); void paths(); + void includepaths(); void getExampleProjectFile(); void expandVars(); @@ -142,6 +143,28 @@ void tst_Config::paths() QCOMPARE(paths[2], rootDir.absoluteFilePath("configs/includes")); } +// Tests whether includepaths are resolved correctly +void tst_Config::includepaths() +{ + auto &config = initConfig(); + const auto docConfig = QFINDTESTDATA("/testdata/configs/includepaths.qdocconf"); + if (!docConfig.isEmpty()) + config.load(docConfig); + + auto rootDir = QFileInfo(docConfig).dir(); + QVERIFY(rootDir.cdUp()); + + const auto paths = config.getCanonicalPathList("includepaths", + Config::IncludePaths); + QVERIFY(paths.size() == 5); + + QCOMPARE(paths[0], "-I" + rootDir.absoluteFilePath("includepaths/include")); + QCOMPARE(paths[0], paths[1]); + QCOMPARE(paths[2], "-I" + rootDir.absoluteFilePath("includepaths/include/more")); + QCOMPARE(paths[3], "-F" + rootDir.absoluteFilePath("includepaths/include/framework")); + QCOMPARE(paths[4], "-isystem" + rootDir.absoluteFilePath("includepaths/include/system")); +} + void::tst_Config::getExampleProjectFile() { auto &config = initConfig(); |