From 5165c037ebbc3948d777595610bc62beb275a9a8 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Thu, 10 Oct 2019 11:04:01 +0200 Subject: [PATCH] Clang: Avoid consuming gcc internal include paths Given the (default) include paths of GCC, e.g. /usr/include/c++/7 /usr/include/x86_64-linux-gnu/c++/7 /usr/include/c++/7/backward /usr/lib/gcc/x86_64-linux-gnu/7/include /usr/local/include /usr/lib/gcc/x86_64-linux-gnu/7/include-fixed /usr/include/x86_64-linux-gnu /usr/include discard gcc-internal paths like /usr/lib/gcc/x86_64-linux-gnu/7/include as they are not relevant for clang and even confuse it with regard to #include_next. Paths below the gcc install dir are considered as gcc-internal. The install dir is queried with $ gcc -print-search-dirs Some GCC distributions, like MinGW, ship the standard library headers in the install dir. Ensure to not discard these. Fixes: QTCREATORBUG-22898 Change-Id: Ia85258fb01b72ad073e71390e003fe8268e3b01f Reviewed-by: Cristian Adam Reviewed-by: Christian Kandeler --- .../cppeditor/cppcodemodelinspectordialog.cpp | 1 + .../cpptools/cppcodemodelinspectordumper.cpp | 1 + .../cpptools/cppprojectinfogenerator.cpp | 1 + src/plugins/cpptools/headerpathfilter.cpp | 29 ++++++++++++ src/plugins/cpptools/headerpathfilter.h | 2 + src/plugins/cpptools/projectpart.h | 2 + src/plugins/projectexplorer/gcctoolchain.cpp | 46 +++++++++++++++++++ src/plugins/projectexplorer/gcctoolchain.h | 4 ++ .../projectexplorer/rawprojectpart.cpp | 1 + src/plugins/projectexplorer/rawprojectpart.h | 2 + src/plugins/projectexplorer/toolchain.h | 2 + tests/unit/unittest/headerpathfilter-test.cpp | 46 +++++++++++++++++++ 12 files changed, 137 insertions(+) diff --git a/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp b/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp index 3da5501302e..461d61a2924 100644 --- a/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp +++ b/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp @@ -1813,6 +1813,7 @@ void CppCodeModelInspectorDialog::updateProjectPartData(const ProjectPart::Ptr & {QString::fromLatin1("ToolChain Type"), part->toolchainType.toString()}, {QString::fromLatin1("ToolChain Target Triple"), part->toolChainTargetTriple}, {QString::fromLatin1("ToolChain Word Width"), CMI::Utils::toString(part->toolChainWordWidth)}, + {QString::fromLatin1("ToolChain Install Dir"), part->toolChainInstallDir.toString()}, {QString::fromLatin1("Language Version"), CMI::Utils::toString(part->languageVersion)}, {QString::fromLatin1("Language Extensions"), CMI::Utils::toString(part->languageExtensions)}, {QString::fromLatin1("Qt Version"), CMI::Utils::toString(part->qtVersion)} diff --git a/src/plugins/cpptools/cppcodemodelinspectordumper.cpp b/src/plugins/cpptools/cppcodemodelinspectordumper.cpp index 0907db98c63..333708c48bf 100644 --- a/src/plugins/cpptools/cppcodemodelinspectordumper.cpp +++ b/src/plugins/cpptools/cppcodemodelinspectordumper.cpp @@ -529,6 +529,7 @@ void Dumper::dumpProjectInfos( const QList &projectInfos) m_out << i3 << "ToolChain Type : " << part->toolchainType.toString() << "\n"; m_out << i3 << "ToolChain Target Triple: " << part->toolChainTargetTriple << "\n"; m_out << i3 << "ToolChain Word Width : " << part->toolChainWordWidth << "\n"; + m_out << i3 << "ToolChain Install Dir : " << part->toolChainInstallDir << "\n"; m_out << i3 << "Compiler Flags : " << part->compilerFlags.join(", ") << "\n"; m_out << i3 << "Selected For Building : " << part->selectedForBuilding << "\n"; m_out << i3 << "Build System Target : " << part->buildSystemTarget << "\n"; diff --git a/src/plugins/cpptools/cppprojectinfogenerator.cpp b/src/plugins/cpptools/cppprojectinfogenerator.cpp index c013e625663..6c33898d0b5 100644 --- a/src/plugins/cpptools/cppprojectinfogenerator.cpp +++ b/src/plugins/cpptools/cppprojectinfogenerator.cpp @@ -167,6 +167,7 @@ ProjectPart::Ptr ProjectInfoGenerator::createProjectPart( part->isMsvc2015Toolchain = tcInfo.isMsvc2015ToolChain; part->toolChainWordWidth = tcInfo.wordWidth == 64 ? ProjectPart::WordWidth64Bit : ProjectPart::WordWidth32Bit; + part->toolChainInstallDir = tcInfo.installDir; part->toolChainTargetTriple = tcInfo.targetTriple; part->extraCodeModelFlags = tcInfo.extraCodeModelFlags; part->compilerFlags = flags.commandLineFlags; diff --git a/src/plugins/cpptools/headerpathfilter.cpp b/src/plugins/cpptools/headerpathfilter.cpp index d401f547f5e..6c2d57fc684 100644 --- a/src/plugins/cpptools/headerpathfilter.cpp +++ b/src/plugins/cpptools/headerpathfilter.cpp @@ -30,9 +30,12 @@ #endif #include +#include #include +#include + namespace CppTools { using ProjectExplorer::HeaderPath; @@ -57,6 +60,31 @@ bool HeaderPathFilter::isProjectHeaderPath(const QString &path) const return path.startsWith(projectDirectory) || path.startsWith(buildDirectory); } +void HeaderPathFilter::removeGccInternalIncludePaths() +{ + if (projectPart.toolchainType != ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID + && projectPart.toolchainType != ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID) { + return; + } + + if (projectPart.toolChainInstallDir.isEmpty()) + return; + + const Utils::FilePath gccInstallDir = projectPart.toolChainInstallDir; + auto isGccInternalInclude = [gccInstallDir](const HeaderPath &headerPath){ + const auto includePath = Utils::FilePath::fromString(headerPath.path); + if (includePath.isChildOf(gccInstallDir)) { + const QString remainingPath = headerPath.path.mid(gccInstallDir.toString().size()); + // MinGW ships the standard library headers in "/include/c++". + // Ensure that we do not remove include paths pointing there. + return !remainingPath.startsWith("/include/c++"); + } + return false; + }; + + Utils::erase(builtInHeaderPaths, isGccInternalInclude); +} + void HeaderPathFilter::filterHeaderPath(const ProjectExplorer::HeaderPath &headerPath) { if (headerPath.path.isEmpty()) @@ -135,6 +163,7 @@ void removeClangSystemHeaderPaths(HeaderPaths &headerPaths) void HeaderPathFilter::tweakHeaderPaths() { removeClangSystemHeaderPaths(builtInHeaderPaths); + removeGccInternalIncludePaths(); auto split = resourceIterator(builtInHeaderPaths, projectPart.toolChainTargetTriple.contains("darwin")); diff --git a/src/plugins/cpptools/headerpathfilter.h b/src/plugins/cpptools/headerpathfilter.h index 20fbc020e88..b1a22b098a6 100644 --- a/src/plugins/cpptools/headerpathfilter.h +++ b/src/plugins/cpptools/headerpathfilter.h @@ -57,6 +57,8 @@ private: bool isProjectHeaderPath(const QString &path) const; + void removeGccInternalIncludePaths(); + static QString ensurePathWithSlashEnding(const QString &path); public: diff --git a/src/plugins/cpptools/projectpart.h b/src/plugins/cpptools/projectpart.h index f7a872e2a1f..d34da3fff93 100644 --- a/src/plugins/cpptools/projectpart.h +++ b/src/plugins/cpptools/projectpart.h @@ -42,6 +42,7 @@ #include #include +#include #include #include @@ -108,6 +109,7 @@ public: bool isMsvc2015Toolchain = false; QString toolChainTargetTriple; ToolChainWordWidth toolChainWordWidth = WordWidth32Bit; + Utils::FilePath toolChainInstallDir; ProjectExplorer::WarningFlags warningFlags = ProjectExplorer::WarningFlags::Default; // Misc diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index 8c1c9e2952b..e7616473986 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -232,6 +232,23 @@ static QString gccVersion(const FilePath &path, const QStringList &env) return QString::fromLocal8Bit(runGcc(path, arguments, env)).trimmed(); } +static Utils::FilePath gccInstallDir(const FilePath &path, const QStringList &env) +{ + const QStringList arguments("-print-search-dirs"); + QString output = QString::fromLocal8Bit(runGcc(path, arguments, env)).trimmed(); + // Expected output looks like this: + // install: /usr/lib/gcc/x86_64-linux-gnu/7/ + // ... + // Note that clang also supports "-print-search-dirs". However, the + // install dir is not part of the output (tested with clang-8/clang-9). + + const QString prefix = "install: "; + const QString line = QTextStream(&output).readLine(); + if (!line.startsWith(prefix)) + return {}; + return Utils::FilePath::fromString(QDir::cleanPath(line.mid(prefix.size()))); +} + // -------------------------------------------------------------------------- // GccToolChain // -------------------------------------------------------------------------- @@ -269,6 +286,15 @@ void GccToolChain::setOriginalTargetTriple(const QString &targetTriple) toolChainUpdated(); } +void GccToolChain::setInstallDir(const Utils::FilePath &installDir) +{ + if (m_installDir == installDir) + return; + + m_installDir = installDir; + toolChainUpdated(); +} + QString GccToolChain::defaultDisplayName() const { QString type = typeDisplayName(); @@ -310,6 +336,13 @@ QString GccToolChain::version() const return m_version; } +FilePath GccToolChain::installDir() const +{ + if (m_installDir.isEmpty()) + m_installDir = detectInstallDir(); + return m_installDir; +} + void GccToolChain::setTargetAbi(const Abi &abi) { if (abi == m_targetAbi) @@ -715,6 +748,7 @@ void GccToolChain::resetToolChain(const FilePath &path) const DetectedAbisResult detectedAbis = detectSupportedAbis(); m_supportedAbis = detectedAbis.supportedAbis; m_originalTargetTriple = detectedAbis.originalTargetTriple; + m_installDir = installDir(); m_targetAbi = Abi(); if (!m_supportedAbis.isEmpty()) { @@ -856,6 +890,13 @@ QString GccToolChain::detectVersion() const return gccVersion(findLocalCompiler(m_compilerCommand, env), env.toStringList()); } +Utils::FilePath GccToolChain::detectInstallDir() const +{ + Environment env = Environment::systemEnvironment(); + addToEnvironment(env); + return gccInstallDir(findLocalCompiler(m_compilerCommand, env), env.toStringList()); +} + // -------------------------------------------------------------------------- // GccToolChainFactory // -------------------------------------------------------------------------- @@ -1119,6 +1160,9 @@ QList GccToolChainFactory::autoDetectToolChain(const ToolChainDescr const GccToolChain::DetectedAbisResult detectedAbis = guessGccAbi(localCompilerPath, systemEnvironment.toStringList(), macros); + const Utils::FilePath installDir = gccInstallDir(localCompilerPath, + systemEnvironment.toStringList()); + for (const Abi &abi : detectedAbis.supportedAbis) { std::unique_ptr tc(dynamic_cast(create())); if (!tc) @@ -1134,6 +1178,7 @@ QList GccToolChainFactory::autoDetectToolChain(const ToolChainDescr tc->setSupportedAbis(detectedAbis.supportedAbis); tc->setTargetAbi(abi); tc->setOriginalTargetTriple(detectedAbis.originalTargetTriple); + tc->setInstallDir(installDir); tc->setDisplayName(tc->defaultDisplayName()); // reset displayname if (!checker || checker(tc.get())) result.append(tc.release()); @@ -1192,6 +1237,7 @@ void GccToolChainConfigWidget::applyImpl() tc->setSupportedAbis(m_abiWidget->supportedAbis()); tc->setTargetAbi(m_abiWidget->currentAbi()); } + tc->setInstallDir(tc->detectInstallDir()); tc->setOriginalTargetTriple(tc->detectSupportedAbis().originalTargetTriple); tc->setDisplayName(displayName); // reset display name tc->setPlatformCodeGenFlags(splitString(m_platformCodeGenFlagsLineEdit->text())); diff --git a/src/plugins/projectexplorer/gcctoolchain.h b/src/plugins/projectexplorer/gcctoolchain.h index 5d46f949f9e..eedba27e57c 100644 --- a/src/plugins/projectexplorer/gcctoolchain.h +++ b/src/plugins/projectexplorer/gcctoolchain.h @@ -71,6 +71,7 @@ public: Abi targetAbi() const override; QString originalTargetTriple() const override; + Utils::FilePath installDir() const override; QString version() const; Abis supportedAbis() const override; void setTargetAbi(const Abi &); @@ -129,6 +130,7 @@ protected: void setCompilerCommand(const Utils::FilePath &path); void setSupportedAbis(const Abis &abis); void setOriginalTargetTriple(const QString &targetTriple); + void setInstallDir(const Utils::FilePath &installDir); void setMacroCache(const QStringList &allCxxflags, const Macros ¯oCache) const; Macros macroCache(const QStringList &allCxxflags) const; @@ -137,6 +139,7 @@ protected: virtual DetectedAbisResult detectSupportedAbis() const; virtual QString detectVersion() const; + virtual Utils::FilePath detectInstallDir() const; // Reinterpret options for compiler drivers inheriting from GccToolChain (e.g qcc) to apply -Wp option // that passes the initial options directly down to the gcc compiler @@ -196,6 +199,7 @@ private: mutable QString m_originalTargetTriple; mutable HeaderPaths m_headerPaths; mutable QString m_version; + mutable Utils::FilePath m_installDir; friend class Internal::GccToolChainConfigWidget; friend class Internal::GccToolChainFactory; diff --git a/src/plugins/projectexplorer/rawprojectpart.cpp b/src/plugins/projectexplorer/rawprojectpart.cpp index 4f24eaf49e2..54274f017cb 100644 --- a/src/plugins/projectexplorer/rawprojectpart.cpp +++ b/src/plugins/projectexplorer/rawprojectpart.cpp @@ -184,6 +184,7 @@ ToolChainInfo::ToolChainInfo(const ToolChain *toolChain, wordWidth = toolChain->targetAbi().wordWidth(); targetTriple = toolChain->originalTargetTriple(); extraCodeModelFlags = toolChain->extraCodeModelFlags(); + installDir = toolChain->installDir(); // ...and save the potentially expensive operations for later so that // they can be run from a worker thread. diff --git a/src/plugins/projectexplorer/rawprojectpart.h b/src/plugins/projectexplorer/rawprojectpart.h index eda3ce5ad7a..aa3a6d376b2 100644 --- a/src/plugins/projectexplorer/rawprojectpart.h +++ b/src/plugins/projectexplorer/rawprojectpart.h @@ -36,6 +36,7 @@ #include #include +#include #include @@ -152,6 +153,7 @@ public: bool isMsvc2015ToolChain = false; unsigned wordWidth = 0; QString targetTriple; + Utils::FilePath installDir; QStringList extraCodeModelFlags; QString sysRootPath; // For headerPathsRunner. diff --git a/src/plugins/projectexplorer/toolchain.h b/src/plugins/projectexplorer/toolchain.h index 4c8b3061ab5..90a1d84e6a2 100644 --- a/src/plugins/projectexplorer/toolchain.h +++ b/src/plugins/projectexplorer/toolchain.h @@ -38,6 +38,7 @@ #include #include +#include #include #include @@ -114,6 +115,7 @@ public: virtual ProjectExplorer::Abis supportedAbis() const; virtual QString originalTargetTriple() const { return QString(); } virtual QStringList extraCodeModelFlags() const { return QStringList(); } + virtual Utils::FilePath installDir() const { return Utils::FilePath(); } virtual bool isValid() const = 0; diff --git a/tests/unit/unittest/headerpathfilter-test.cpp b/tests/unit/unittest/headerpathfilter-test.cpp index 79091e0a0b2..dac2974ecf8 100644 --- a/tests/unit/unittest/headerpathfilter-test.cpp +++ b/tests/unit/unittest/headerpathfilter-test.cpp @@ -27,6 +27,7 @@ #include #include +#include namespace { @@ -248,6 +249,51 @@ TEST_F(HeaderPathFilter, ClangHeadersAndCppIncludesPathsOrderLinux) HasBuiltIn("/builtin_path"))); } +// Include paths below the installation dir should be removed as they confuse clang. +TEST_F(HeaderPathFilter, RemoveGccInternalPaths) +{ + projectPart.toolChainInstallDir = Utils::FilePath::fromUtf8("/usr/lib/gcc/x86_64-linux-gnu/7"); + projectPart.toolchainType = ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID; + projectPart.headerPaths = { + HeaderPath{"/usr/lib/gcc/x86_64-linux-gnu/7/include", HeaderPathType::BuiltIn}, + HeaderPath{"/usr/lib/gcc/x86_64-linux-gnu/7/include-fixed", HeaderPathType::BuiltIn}, + }; + CppTools::HeaderPathFilter filter{projectPart, + CppTools::UseTweakedHeaderPaths::Yes, + "6.0", + CLANG_RESOURCE_DIR}; + + filter.process(); + + ASSERT_THAT(filter.builtInHeaderPaths, ElementsAre(HasBuiltIn(CLANG_RESOURCE_DIR))); +} + +// MinGW ships the standard library headers in "/include/c++". +// Ensure that we do not remove include paths pointing there. +TEST_F(HeaderPathFilter, RemoveGccInternalPathsExceptForStandardPaths) +{ + projectPart.toolChainInstallDir = Utils::FilePath::fromUtf8( + "c:/mingw730_64/lib/gcc/x86_64-w64-mingw32/7.3.0"); + projectPart.toolchainType = ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID; + projectPart.headerPaths = { + HeaderPath{"c:/mingw730_64/lib/gcc/x86_64-w64-mingw32/7.3.0/include/c++", HeaderPathType::BuiltIn}, + HeaderPath{"c:/mingw730_64/lib/gcc/x86_64-w64-mingw32/7.3.0/include/c++/x86_64-w64-mingw32", HeaderPathType::BuiltIn}, + HeaderPath{"c:/mingw730_64/lib/gcc/x86_64-w64-mingw32/7.3.0/include/c++/backward", HeaderPathType::BuiltIn}, + }; + + auto expected = projectPart.headerPaths; + expected << HeaderPath{CLANG_RESOURCE_DIR, HeaderPathType::BuiltIn}; + + CppTools::HeaderPathFilter filter{projectPart, + CppTools::UseTweakedHeaderPaths::Yes, + "6.0", + CLANG_RESOURCE_DIR}; + + filter.process(); + + ASSERT_THAT(filter.builtInHeaderPaths, ContainerEq(expected)); +} + TEST_F(HeaderPathFilter, ClangHeadersAndCppIncludesPathsOrderNoVersion) { projectPart.headerPaths = {