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 <cristian.adam@qt.io>
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Nikolai Kosjar
2019-10-10 11:04:01 +02:00
parent 7a87dc0484
commit 5165c037eb
12 changed files with 137 additions and 0 deletions

View File

@@ -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)}

View File

@@ -529,6 +529,7 @@ void Dumper::dumpProjectInfos( const QList<ProjectInfo> &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";

View File

@@ -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;

View File

@@ -30,9 +30,12 @@
#endif
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <QRegularExpression>
#include <utils/algorithm.h>
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 "<installdir>/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"));

View File

@@ -57,6 +57,8 @@ private:
bool isProjectHeaderPath(const QString &path) const;
void removeGccInternalIncludePaths();
static QString ensurePathWithSlashEnding(const QString &path);
public:

View File

@@ -42,6 +42,7 @@
#include <cplusplus/Token.h>
#include <utils/cpplanguage_details.h>
#include <utils/fileutils.h>
#include <QString>
#include <QSharedPointer>
@@ -108,6 +109,7 @@ public:
bool isMsvc2015Toolchain = false;
QString toolChainTargetTriple;
ToolChainWordWidth toolChainWordWidth = WordWidth32Bit;
Utils::FilePath toolChainInstallDir;
ProjectExplorer::WarningFlags warningFlags = ProjectExplorer::WarningFlags::Default;
// Misc

View File

@@ -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<ToolChain *> 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<GccToolChain> tc(dynamic_cast<GccToolChain *>(create()));
if (!tc)
@@ -1134,6 +1178,7 @@ QList<ToolChain *> 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()));

View File

@@ -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 &macroCache) 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;

View File

@@ -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.

View File

@@ -36,6 +36,7 @@
#include <utils/cpplanguage_details.h>
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <QPointer>
@@ -152,6 +153,7 @@ public:
bool isMsvc2015ToolChain = false;
unsigned wordWidth = 0;
QString targetTriple;
Utils::FilePath installDir;
QStringList extraCodeModelFlags;
QString sysRootPath; // For headerPathsRunner.

View File

@@ -38,6 +38,7 @@
#include <utils/cpplanguage_details.h>
#include <utils/environment.h>
#include <utils/fileutils.h>
#include <QObject>
#include <QSet>
@@ -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;

View File

@@ -27,6 +27,7 @@
#include <cpptools/headerpathfilter.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorerconstants.h>
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 "<installdir>/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 = {