kde-extraapps/kdevelop/languages/cpp/includepathcomputer.cpp

206 lines
7 KiB
C++
Raw Normal View History

/*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "includepathcomputer.h"
#include "cpputils.h"
#include <interfaces/iproject.h>
#include <interfaces/icore.h>
#include <interfaces/iprojectcontroller.h>
#include <project/projectmodel.h>
#include <project/interfaces/ibuildsystemmanager.h>
#include <language/duchain/duchainlock.h>
#include <language/duchain/duchain.h>
#include <language/duchain/topducontext.h>
#include <language/interfaces/idefinesandincludesmanager.h>
#include "cppduchain/environmentmanager.h"
#include <KLocalizedString>
#include <QThread>
#include <QCoreApplication>
using namespace KDevelop;
const bool enableIncludePathResolution = true;
IncludePathComputer::IncludePathComputer(const QString& file)
: m_source(file)
, m_ready(false)
, m_gotPathsFromManager(false)
{
}
void IncludePathComputer::computeForeground()
{
Q_ASSERT(QThread::currentThread() == qApp->thread());
if (CppUtils::headerExtensions().contains(QFileInfo(m_source).suffix())) {
// This file is a header. Since a header doesn't represent a target, we just try to get
// the include-paths for the corresponding source-file, if there is one.
QString newSource = CppUtils::sourceOrHeaderCandidate(m_source, true);
if (!newSource.isEmpty()) {
m_source = newSource;
}
}
if (m_source.isEmpty()) {
kDebug() << "cannot compute include-paths without source-file";
return;
}
const IndexedString indexedSource(m_source);
bool noProject = true;
foreach (IProject *project, ICore::self()->projectController()->projects()) {
QList<ProjectFileItem*> files = project->filesForPath(indexedSource);
if (files.isEmpty()) {
continue;
}
noProject = false;
IBuildSystemManager* buildManager = project->buildSystemManager();
if (!buildManager) {
// We found the project, but no build manager!!
kDebug() << "didn't get build manager for project:" << project->name();
continue;
}
auto idm = IDefinesAndIncludesManager::manager();
ProjectFileItem* file = files.last();
Path::List dirs;
// A file might be defined in different targets.
// Prefer file items defined inside a target with non-empty includes.
foreach (ProjectFileItem* f, files) {
if (!dynamic_cast<ProjectTargetItem*>(f->parent())) {
continue;
}
file = f;
dirs = idm->includes(f, IDefinesAndIncludesManager::ProjectSpecific);
if (!dirs.isEmpty()) {
break;
}
}
// otherwise we did not find a target, so just use any include paths provided by the manager
if (dirs.isEmpty()) {
dirs = idm->includes(file, IDefinesAndIncludesManager::ProjectSpecific);
}
m_projectName = project->name();
m_projectDirectory = project->path();
m_effectiveBuildDirectory = m_buildDirectory = buildManager->buildDirectory(project->projectItem());
kDebug(9007) << "Got build-directory from project manager:" << m_effectiveBuildDirectory;
if (m_projectDirectory == m_effectiveBuildDirectory) {
m_projectDirectory.clear();
m_effectiveBuildDirectory.clear();
}
for (const auto& dir : idm->includes(file, IDefinesAndIncludesManager::Type(IDefinesAndIncludesManager::UserDefined))){
addInclude(dir);
}
m_defines = idm->defines(file);
m_gotPathsFromManager = !dirs.isEmpty();
kDebug(9007) << "Got " << dirs.count() << " include-paths from build-manager";
foreach (const Path& dir, dirs) {
addInclude(dir);
}
for (const auto& dir : idm->includes(file, IDefinesAndIncludesManager::Type(IDefinesAndIncludesManager::CompilerSpecific))){
addInclude(dir);
}
}
if (noProject) {
for (const auto& dir : IDefinesAndIncludesManager::manager()->includes(nullptr)) {
addInclude( dir );
}
m_defines = IDefinesAndIncludesManager::manager()->defines(nullptr);
} else if (!m_gotPathsFromManager) {
kDebug(9007) << "Did not find any include paths from project manager for" << m_source;
}
}
void IncludePathComputer::computeBackground()
{
if (m_ready) {
return;
}
if (!enableIncludePathResolution) {
m_ready = true;
return;
}
if (m_effectiveBuildDirectory.isValid()) {
m_includeResolver.setOutOfSourceBuildSystem(m_projectDirectory.toLocalFile(), m_effectiveBuildDirectory.toLocalFile());
} else {
if (m_projectDirectory.isValid()) {
//Report that the build-manager did not return the build-directory, for debugging
kDebug(9007) << "Build manager for project %1 did not return a build directory" << m_projectName;
}
m_includeResolver.resetOutOfSourceBuild();
}
m_includePathDependency = m_includeResolver.findIncludePathDependency(m_source);
kDebug() << "current include path dependency state:" << m_includePathDependency.toString();
// only look at make when we did not get any paths from the build manager
m_includeResolver.enableMakeResolution(!m_gotPathsFromManager);
CppTools::PathResolutionResult result = m_includeResolver.resolveIncludePath(m_source);
m_includePathDependency = result.includePathDependency;
kDebug() << "new include path dependency:" << m_includePathDependency.toString();
foreach (const QString &res, result.paths) {
addInclude(Path(res));
}
if (!result) {
kDebug(9007) << "Failed to resolve include-path for \"" << m_source << "\":"
<< result.errorMessage << "\n" << result.longErrorMessage << "\n";
// Last chance: Take a parsed version of the file from the du-chain, and get its include-paths
// We will then get the include-path that some time was used to parse the file
// NOTE: this is important for library headers, as when we open them they are not opened in any project
// nor do they have a build folder which we could use
DUChainReadLocker readLock;
foreach(const ParsingEnvironmentFilePointer& file, DUChain::self()->allEnvironmentFiles(IndexedString(m_source))) {
auto envFile = dynamic_cast<const Cpp::EnvironmentFile*>(file.data());
if (envFile && !envFile->includePaths().isEmpty()) {
foreach(const IndexedString& path, envFile->includePaths()) {
addInclude(Path(path.toUrl()));
}
kDebug(9007) << "Took include-path for" << m_source << "from a random parsed duchain-version of it";
break;
}
}
}
m_ready = true;
}
void IncludePathComputer::addInclude(const Path& path)
{
if (!m_hasPath.contains(path)) {
m_ret << path;
m_hasPath.insert(path);
}
}