mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-26 11:52:54 +00:00
206 lines
7 KiB
C++
206 lines
7 KiB
C++
![]() |
/*
|
||
|
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);
|
||
|
}
|
||
|
}
|