diff options
author | Topi Reinio <topi.reinio@qt.io> | 2021-06-09 12:24:26 +0200 |
---|---|---|
committer | Topi Reinio <topi.reinio@qt.io> | 2021-06-14 13:49:18 +0200 |
commit | fd56be29b43929a1a87211db3f6ff98c58af1429 (patch) | |
tree | b04f99241436a8943db62f1a8c4c62517c5d07ab | |
parent | 3e88b552a1aac807504fa85bf752e6bd8542b573 (diff) |
qdoc: Query the compiler for system and framework include paths on macOS
Depending on the version, libclang is not able to resolve the system
include paths on macOS; Fetch the include paths from the compiler by
calling 'clang++ -E -x c++ - -v', parsing the output, and passing them
as additional arguments when parsing the sources.
Note that this requires the correct version of the compiler to be
available under PATH.
Fixes: QTBUG-94365
Change-Id: Iaa1b9869d3be0b4fcb8df00b47bb43a80032aa02
Reviewed-by: Paul Wicking <paul.wicking@qt.io>
-rw-r--r-- | src/qdoc/clangcodeparser.cpp | 10 | ||||
-rw-r--r-- | src/qdoc/utilities.cpp | 99 | ||||
-rw-r--r-- | src/qdoc/utilities.h | 1 |
3 files changed, 109 insertions, 1 deletions
diff --git a/src/qdoc/clangcodeparser.cpp b/src/qdoc/clangcodeparser.cpp index f037e4ed8..46c237114 100644 --- a/src/qdoc/clangcodeparser.cpp +++ b/src/qdoc/clangcodeparser.cpp @@ -1088,14 +1088,22 @@ Node *ClangVisitor::nodeForCommentAtLocation(CXSourceLocation loc, CXSourceLocat Get the include paths from the qdoc configuration database \a config. Call the initializeParser() in the base class. Get the defines list from the qdocconf database. + + \note on \macos, we try to also query the system/framework + include paths from the compiler. */ void ClangCodeParser::initializeParser() { Config &config = Config::instance(); m_version = config.getString(CONFIG_VERSION); - const auto args = config.getStringList(CONFIG_INCLUDEPATHS); + auto args = config.getStringList(CONFIG_INCLUDEPATHS); QSet<QString> seen; m_includePaths.clear(); + +#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; diff --git a/src/qdoc/utilities.cpp b/src/qdoc/utilities.cpp index 19e1572f6..f82282b9b 100644 --- a/src/qdoc/utilities.cpp +++ b/src/qdoc/utilities.cpp @@ -26,6 +26,7 @@ ** ****************************************************************************/ +#include <QtCore/qprocess.h> #include "utilities.h" QT_BEGIN_NAMESPACE @@ -102,6 +103,104 @@ QString comma(qsizetype wordPosition, qsizetype numberOfWords) return QStringLiteral(", and "); } +/*! + \internal +*/ +static bool runProcess(const QString &program, const QStringList &arguments, + QByteArray *stdOutIn, QByteArray *stdErrIn) +{ + QProcess process; + process.start(program, arguments, QProcess::ReadWrite); + if (!process.waitForStarted()) { + qCDebug(lcQdoc).nospace() << "Unable to start " << process.program() + << ": " << process.errorString(); + return false; + } + process.closeWriteChannel(); + const bool finished = process.waitForFinished(); + const QByteArray stdErr = process.readAllStandardError(); + if (stdErrIn) + *stdErrIn = stdErr; + if (stdOutIn) + *stdOutIn = process.readAllStandardOutput(); + + if (!finished) { + qCDebug(lcQdoc).nospace() << process.program() << " timed out: " << stdErr; + process.kill(); + return false; + } + + if (process.exitStatus() != QProcess::NormalExit) { + qCDebug(lcQdoc).nospace() << process.program() << " crashed: " << stdErr; + return false; + } + + if (process.exitCode() != 0) { + qCDebug(lcQdoc).nospace() << process.program() << " exited with " + << process.exitCode() << ": " << stdErr; + return false; + } + + return true; +} + +/*! + \internal +*/ +static QByteArray frameworkSuffix() { + return QByteArrayLiteral(" (framework directory)"); +} + +/*! + \internal + Determine the compiler's internal include paths from the output of + + \badcode + [clang++|g++] -E -x c++ - -v </dev/null + \endcode + + Output looks like: + + \badcode + #include <...> search starts here: + /usr/local/include + /System/Library/Frameworks (framework directory) + End of search list. + \endcode +*/ +QStringList getInternalIncludePaths(const QString &compiler) +{ + QStringList result; + QStringList arguments; + arguments << QStringLiteral("-E") << QStringLiteral("-x") << QStringLiteral("c++") + << QStringLiteral("-") << QStringLiteral("-v"); + QByteArray stdOut; + QByteArray stdErr; + if (!runProcess(compiler, arguments, &stdOut, &stdErr)) + return result; + const QByteArrayList stdErrLines = stdErr.split('\n'); + bool isIncludeDir = false; + for (const QByteArray &line : stdErrLines) { + if (isIncludeDir) { + if (line.startsWith(QByteArrayLiteral("End of search list"))) { + isIncludeDir = false; + } else { + QByteArray prefix("-I"); + QByteArray headerPath{line.trimmed()}; + if (headerPath.endsWith(frameworkSuffix())) { + headerPath.truncate(headerPath.size() - frameworkSuffix().size()); + prefix = QByteArrayLiteral("-F"); + } + result.append(QString::fromLocal8Bit(prefix + headerPath)); + } + } else if (line.startsWith(QByteArrayLiteral("#include <...> search starts here"))) { + isIncludeDir = true; + } + } + + return result; +} + } // namespace Utilities QT_END_NAMESPACE diff --git a/src/qdoc/utilities.h b/src/qdoc/utilities.h index f820c39bd..488c960ad 100644 --- a/src/qdoc/utilities.h +++ b/src/qdoc/utilities.h @@ -44,6 +44,7 @@ bool debugging(); QString separator(qsizetype wordPosition, qsizetype numberOfWords); QString comma(qsizetype wordPosition, qsizetype numberOfWords); +QStringList getInternalIncludePaths(const QString &compiler); } QT_END_NAMESPACE |