diff --git a/plugins/clang/clangsupport.cpp b/plugins/clang/clangsupport.cpp --- a/plugins/clang/clangsupport.cpp +++ b/plugins/clang/clangsupport.cpp @@ -181,11 +181,10 @@ { const auto builtinDir = ClangHelpers::clangBuiltinIncludePath(); - const auto headerToCheck = QLatin1String("cpuid.h"); - if (!QFile::exists(builtinDir + QLatin1Char('/') + headerToCheck)) { - setErrorDescription(i18n("The clang builtin include path \"%1\" is invalid (missing %2 header).\n" + if (!ClangHelpers::isValidClangBuiltingIncludePath(builtinDir)) { + setErrorDescription(i18n("The clang builtin include path \"%1\" is invalid (missing cpuid.h header).\n" "Try setting the KDEV_CLANG_BUILTIN_DIR environment variable manually to fix this.\n" - "See also: https://bugs.kde.org/show_bug.cgi?id=393779", builtinDir, headerToCheck)); + "See also: https://bugs.kde.org/show_bug.cgi?id=393779", builtinDir)); return; } } diff --git a/plugins/clang/duchain/clanghelpers.h b/plugins/clang/duchain/clanghelpers.h --- a/plugins/clang/duchain/clanghelpers.h +++ b/plugins/clang/duchain/clanghelpers.h @@ -104,11 +104,20 @@ /** * @return The path containing Clang built includes (e.g. stddef.h, stdarg.h, cpuid.h) + * The returned path is the env. var KDEV_CLANG_BUILTIN_DIR when set otherwise the path + * to the headers used when kdev-clang was built, possibly updated for upgrades to + * the library (e.g. 7.0.0 -> 7.0.1). + * Returns an empty string if none of the checked locations contain the file cpuid.h . * * Also see: https://clang.llvm.org/docs/FAQ.html */ KDEVCLANGPRIVATE_EXPORT QString clangBuiltinIncludePath(); +/** + * @return True if the given @a path is a valid clang builtin directory. + */ +KDEVCLANGPRIVATE_EXPORT bool isValidClangBuiltingIncludePath(const QString& path); + } #endif //CLANGHELPERS_H diff --git a/plugins/clang/duchain/clanghelpers.cpp b/plugins/clang/duchain/clanghelpers.cpp --- a/plugins/clang/duchain/clanghelpers.cpp +++ b/plugins/clang/duchain/clanghelpers.cpp @@ -369,21 +369,39 @@ return clangVersion; } +bool ClangHelpers::isValidClangBuiltingIncludePath(const QString& path) +{ + return QFile::exists(path + QLatin1String("/cpuid.h")); +} + QString ClangHelpers::clangBuiltinIncludePath() { + // use a lambda to store the result in a static variable which can be + // returned without recomputing the string on subsequent calls. static const auto dir = []() -> QString { auto dir = QString::fromUtf8(qgetenv("KDEV_CLANG_BUILTIN_DIR")); - if (!dir.isEmpty()) { + if (!dir.isEmpty() && isValidClangBuiltingIncludePath(dir)) { clangDebug() << "Using dir from $KDEV_CLANG_BUILTIN_DIR:" << dir; return dir; } #ifdef Q_OS_WIN32 // attempt to use the bundled copy on Windows dir = QDir::cleanPath(QStringLiteral("%1/../lib/clang/%2/include") .arg(QCoreApplication::applicationDirPath(), clangVersion())); - clangDebug() << "Trying" << dir; - if (QFileInfo(dir).isDir()) { + if (isValidClangBuiltingIncludePath(dir)) { + clangDebug() << "Using builtin dir:" << dir; + return dir; + } +#elif defined(Q_OS_UNIX) + // a clang version upgrade since we were last built can + // cause problems if the "clang/$fullversion/include" path component + // changed. Try to generate the correct builtin_dir for the current + // major.minor.patchlevel version: pop the last 2 components then + // chdir through with the updated version directory. + dir = QDir::cleanPath(QStringLiteral(KDEV_CLANG_BUILTIN_DIR "/../../%1/include").arg(clangVersion())); + if (isValidClangBuiltingIncludePath(dir)) { + clangDebug() << "Using builtin dir:" << dir; return dir; } #endif