Commit 46c0ea43 authored by René J.V. Bertin's avatar René J.V. Bertin
Browse files

support -iframework and -F header search path options

REVIEW: 128272
parent 94ed2610
......@@ -140,7 +140,7 @@ ProjectFileItem* findProjectFileItem(const IndexedString& url, bool* hasBuildSys
}
if (file && file->project()) {
if (auto bsm = file->project()->buildSystemManager()) {
*hasBuildSystemInfo = bsm->hasIncludesOrDefines(file);
*hasBuildSystemInfo = bsm->hasBuildInfo(file);
}
}
return file;
......@@ -165,10 +165,12 @@ ClangParseJob::ClangParseJob(const IndexedString& url, ILanguageSupport* languag
bool hasBuildSystemInfo;
if (auto file = findProjectFileItem(tuUrl, &hasBuildSystemInfo)) {
m_environment.addIncludes(IDefinesAndIncludesManager::manager()->includes(file));
m_environment.addFrameworkDirectories(IDefinesAndIncludesManager::manager()->frameworkDirectories(file));
m_environment.addDefines(IDefinesAndIncludesManager::manager()->defines(file));
m_environment.setParserSettings(ClangSettingsManager::self()->parserSettings(file));
} else {
m_environment.addIncludes(IDefinesAndIncludesManager::manager()->includes(tuUrl.str()));
m_environment.addFrameworkDirectories(IDefinesAndIncludesManager::manager()->frameworkDirectories(tuUrl.str()));
m_environment.addDefines(IDefinesAndIncludesManager::manager()->defines(tuUrl.str()));
m_environment.setParserSettings(ClangSettingsManager::self()->parserSettings(tuUrl.str()));
}
......@@ -230,6 +232,7 @@ void ClangParseJob::run(ThreadWeaver::JobPointer /*self*/, ThreadWeaver::Thread*
}
m_environment.addIncludes(IDefinesAndIncludesManager::manager()->includesInBackground(tuUrlStr));
m_environment.addFrameworkDirectories(IDefinesAndIncludesManager::manager()->frameworkDirectoriesInBackground(tuUrlStr));
m_environment.addDefines(IDefinesAndIncludesManager::manager()->definesInBackground(tuUrlStr));
m_environment.setPchInclude(userDefinedPchIncludeForFile(tuUrlStr));
}
......
......@@ -43,14 +43,20 @@ void ClangParsingEnvironment::addIncludes(const Path::List& includes)
m_includes += includes;
}
ClangParsingEnvironment::IncludePaths ClangParsingEnvironment::includes() const
void ClangParsingEnvironment::addFrameworkDirectories(const KDevelop::Path::List& frameworkDirectories)
{
m_frameworkDirectories += frameworkDirectories;
}
template <typename PathType>
static PathType appendPaths(const KDevelop::Path::List &paths, const KDevelop::Path::List &projectPaths)
{
IncludePaths ret;
ret.project.reserve(m_includes.size());
ret.system.reserve(m_includes.size());
foreach (const auto& path, m_includes) {
PathType ret;
ret.project.reserve(paths.size());
ret.system.reserve(paths.size());
foreach (const auto& path, paths) {
bool inProject = false;
foreach (const auto& project, m_projectPaths) {
foreach (const auto& project, projectPaths) {
if (project.isParentOf(path) || project == path) {
inProject = true;
break;
......@@ -65,6 +71,16 @@ ClangParsingEnvironment::IncludePaths ClangParsingEnvironment::includes() const
return ret;
}
ClangParsingEnvironment::IncludePaths ClangParsingEnvironment::includes() const
{
return appendPaths<IncludePaths>(m_includes, m_projectPaths);
}
ClangParsingEnvironment::FrameworkDirectories ClangParsingEnvironment::frameworkDirectories() const
{
return appendPaths<FrameworkDirectories>(m_frameworkDirectories, m_projectPaths);
}
void ClangParsingEnvironment::addDefines(const QHash<QString, QString>& defines)
{
for (auto it = defines.constBegin(); it != defines.constEnd(); ++it) {
......@@ -121,6 +137,11 @@ uint ClangParsingEnvironment::hash() const
hash << qHash(include);
}
hash << m_frameworkDirectories.size();
for (const auto& fwDir : m_frameworkDirectories) {
hash << qHash(fwDir);
}
hash << qHash(m_pchInclude);
hash << qHash(m_parserSettings.parserOptions);
return hash;
......@@ -130,6 +151,7 @@ bool ClangParsingEnvironment::operator==(const ClangParsingEnvironment& other) c
{
return m_defines == other.m_defines
&& m_includes == other.m_includes
&& m_frameworkDirectories == other.m_frameworkDirectories
&& m_pchInclude == other.m_pchInclude
&& m_quality == other.m_quality
&& m_tuUrl == other.m_tuUrl
......
......@@ -49,6 +49,11 @@ public:
*/
void addIncludes(const KDevelop::Path::List& includes);
/**
* Add the given list of @p framework-directories to this environment.
*/
void addFrameworkDirectories(const KDevelop::Path::List& frameworkDirectories);
struct IncludePaths
{
/// This list contains all include paths outside the known projects paths.
......@@ -61,6 +66,18 @@ public:
*/
IncludePaths includes() const;
struct FrameworkDirectories
{
/// This list contains all framework directories outside the known projects paths.
KDevelop::Path::List system;
/// This list contains all framework directories inside the known projects paths.
KDevelop::Path::List project;
};
/**
* Returns the list of framework directories, split into a list of system paths and project paths.
*/
FrameworkDirectories frameworkDirectories() const;
void addDefines(const QHash<QString, QString>& defines);
QMap<QString, QString> defines() const;
......@@ -101,6 +118,7 @@ public:
private:
KDevelop::Path::List m_projectPaths;
KDevelop::Path::List m_includes;
KDevelop::Path::List m_frameworkDirectories;
// NOTE: As elements in QHash stored in an unordered sequence, we're using QMap instead
QMap<QString, QString> m_defines;
KDevelop::Path m_pchInclude;
......
......@@ -118,6 +118,28 @@ void addIncludes(QVector<const char*>* args, QVector<QByteArray>* otherArgs,
}
}
void addFrameworkDirectories(QVector<const char*>* args, QVector<QByteArray>* otherArgs,
const Path::List& frameworkDirectories, const char* cliSwitch)
{
foreach (const Path& url, frameworkDirectories) {
if (url.isEmpty()) {
continue;
}
QFileInfo info(url.toLocalFile());
if (!info.isDir()) {
qWarning() << "supposed framework directory is not a directory:" << url.pathOrUrl();
continue;
}
QByteArray path = url.toLocalFile().toUtf8();
otherArgs->append(cliSwitch);
otherArgs->append(path);
args->append(cliSwitch);
args->append(path.constData());
}
}
QVector<CXUnsavedFile> toClangApi(const QVector<UnsavedFile>& unsavedFiles)
{
QVector<CXUnsavedFile> unsaved;
......@@ -220,6 +242,10 @@ ParseSessionData::ParseSessionData(const QVector<UnsavedFile>& unsavedFiles, Cla
addIncludes(&clangArguments, &smartArgs, includes.system, "-isystem");
addIncludes(&clangArguments, &smartArgs, includes.project, "-I");
const auto& frameworkDirectories = environment.frameworkDirectories();
addFrameworkDirectories(&clangArguments, &smartArgs, frameworkDirectories.system, "-iframework");
addFrameworkDirectories(&clangArguments, &smartArgs, frameworkDirectories.project, "-F");
smartArgs << writeDefinesFile(environment.defines());
clangArguments << "-imacros" << smartArgs.last().constData();
......
......@@ -152,6 +152,11 @@ Path::List CompilerProvider::includes( ProjectBaseItem* item ) const
return config.compiler->includes(languageType == Utils::C ? config.parserArguments.cArguments : config.parserArguments.cppArguments);
}
Path::List CompilerProvider::frameworkDirectories( ProjectBaseItem* item ) const
{
return {};
}
IDefinesAndIncludesManager::Type CompilerProvider::type() const
{
return IDefinesAndIncludesManager::CompilerSpecific;
......
......@@ -40,6 +40,7 @@ public:
KDevelop::Defines defines( KDevelop::ProjectBaseItem* item ) const override;
KDevelop::Path::List includes( KDevelop::ProjectBaseItem* item ) const override;
KDevelop::Path::List frameworkDirectories( KDevelop::ProjectBaseItem* item ) const override;
KDevelop::IDefinesAndIncludesManager::Type type() const override;
/// @return current compiler for the @p item
......
......@@ -111,6 +111,10 @@ DefinesAndIncludesManager::DefinesAndIncludesManager( QObject* parent, const QVa
{
KDEV_USE_EXTENSION_INTERFACE(IDefinesAndIncludesManager);
registerProvider(m_settings->provider());
#ifdef Q_OS_OSX
m_defaultFrameworkDirectories += Path(QStringLiteral("/Library/Frameworks"));
m_defaultFrameworkDirectories += Path(QStringLiteral("/System/Library/Frameworks"));
#endif
}
DefinesAndIncludesManager::~DefinesAndIncludesManager() = default;
......@@ -184,6 +188,32 @@ Path::List DefinesAndIncludesManager::includes( ProjectBaseItem* item, Type type
return includes;
}
Path::List DefinesAndIncludesManager::frameworkDirectories( ProjectBaseItem* item, Type type ) const
{
Q_ASSERT(QThread::currentThread() == qApp->thread());
if (!item) {
return m_settings->provider()->frameworkDirectories(nullptr);
}
Path::List frameworkDirectories = m_defaultFrameworkDirectories;
if ( type & ProjectSpecific ) {
auto buildManager = item->project()->buildSystemManager();
if ( buildManager ) {
frameworkDirectories += buildManager->frameworkDirectories(item);
}
}
for (auto provider : m_providers) {
if (provider->type() & type) {
frameworkDirectories += provider->frameworkDirectories(item);
}
}
return frameworkDirectories;
}
bool DefinesAndIncludesManager::unregisterProvider(IDefinesAndIncludesManager::Provider* provider)
{
int idx = m_providers.indexOf(provider);
......@@ -218,6 +248,11 @@ Path::List DefinesAndIncludesManager::includes(const QString& path) const
+ m_noProjectIPM->includesAndDefines(path).first;
}
Path::List DefinesAndIncludesManager::frameworkDirectories(const QString& path) const
{
return m_settings->provider()->frameworkDirectories(nullptr);
}
void DefinesAndIncludesManager::openConfigurationDialog(const QString& pathToFile)
{
if (auto project = KDevelop::ICore::self()->projectController()->findProjectForUrl(QUrl::fromLocalFile(pathToFile))) {
......@@ -238,6 +273,17 @@ Path::List DefinesAndIncludesManager::includesInBackground(const QString& path)
return includes;
}
Path::List DefinesAndIncludesManager::frameworkDirectoriesInBackground(const QString& path) const
{
Path::List fwDirs;
for (auto provider: m_backgroundProviders) {
fwDirs += provider->frameworkDirectoriesInBackground(path);
}
return fwDirs;
}
Defines DefinesAndIncludesManager::definesInBackground(const QString& path) const
{
QHash<QString, QString> defines;
......
......@@ -49,14 +49,18 @@ public:
KDevelop::Defines defines( KDevelop::ProjectBaseItem* item, Type type ) const override;
///@return list of all custom includes for @p item
KDevelop::Path::List includes( KDevelop::ProjectBaseItem* item, Type type ) const override;
///@return list of all custom framework directories for @p item
KDevelop::Path::List frameworkDirectories( KDevelop::ProjectBaseItem* item, Type type ) const override;
KDevelop::Defines defines( const QString& path ) const override;
KDevelop::Path::List includes( const QString& path ) const override;
KDevelop::Path::List frameworkDirectories(const QString& path) const override;
void registerProvider( Provider* provider ) override;
bool unregisterProvider( Provider* provider ) override;
KDevelop::Path::List includesInBackground( const QString& path ) const override;
KDevelop::Path::List frameworkDirectoriesInBackground( const QString& path ) const override;
KDevelop::Defines definesInBackground(const QString& path) const override;
void registerBackgroundProvider(BackgroundProvider* provider) override;
......@@ -78,6 +82,7 @@ private:
QVector<BackgroundProvider*> m_backgroundProviders;
SettingsManager* m_settings;
QScopedPointer<NoProjectIncludePathsManager> m_noProjectIPM;
KDevelop::Path::List m_defaultFrameworkDirectories;
};
#endif // CUSTOMDEFINESANDINCLUDESMANAGER_H
......@@ -72,6 +72,8 @@ public:
virtual Path::List includes( ProjectBaseItem* item ) const = 0;
virtual Path::List frameworkDirectories( ProjectBaseItem* item ) const = 0;
/// @return the type of i/d this provider provides
virtual Type type() const = 0;
};
......@@ -90,6 +92,8 @@ public:
virtual Path::List includesInBackground( const QString& path ) const = 0;
virtual Path::List frameworkDirectoriesInBackground( const QString& path ) const = 0;
virtual Defines definesInBackground( const QString& path ) const = 0;
/// @return the type of i/d this provider provides
......@@ -106,6 +110,11 @@ public:
///NOTE: call it from the foreground thread only.
virtual Path::List includes( ProjectBaseItem* item, Type type = All ) const = 0;
///@param item project item
///@return list of framework directories for @p item
///NOTE: call it from the foreground thread only.
virtual Path::List frameworkDirectories( ProjectBaseItem* item, Type type = All ) const = 0;
///@param path path to an out-of-project file.
///@return list of defines for @p path
///NOTE: call it from the foreground thread only.
......@@ -116,6 +125,11 @@ public:
///NOTE: call it from the foreground thread only.
virtual Path::List includes( const QString& path ) const = 0;
///@param path path to an out-of-project file.
///@return list of framework directories for @p path
///NOTE: call it from the foreground thread only.
virtual Path::List frameworkDirectories( const QString& path ) const = 0;
/**
* Computes include directories in background thread.
*
......@@ -125,6 +139,15 @@ public:
**/
virtual Path::List includesInBackground( const QString& path ) const = 0;
/**
* Computes framework directories in background thread.
*
* This is especially useful for CustomMake projects.
*
* Call it from background thread if possible.
**/
virtual Path::List frameworkDirectoriesInBackground( const QString& path ) const = 0;
/**
* Computes defined macros in background thread.
*
......
......@@ -86,6 +86,7 @@ CMakeJsonData importCommands(const Path& commandsFile)
CMakeFile ret;
ret.includes = result.paths;
ret.frameworkDirectories = result.frameworkDirectories;
ret.defines = result.defines;
// NOTE: we use the canonical file path to prevent issues with symlinks in the path
// leading to lookup failures
......
......@@ -124,7 +124,7 @@ CMakeManager::~CMakeManager()
parseLock()->unlock();
}
bool CMakeManager::hasIncludesOrDefines(ProjectBaseItem* item) const
bool CMakeManager::hasBuildInfo(ProjectBaseItem* item) const
{
return m_projects[item->project()].jsonData.files.contains(item->path());
}
......@@ -234,6 +234,11 @@ Path::List CMakeManager::includeDirectories(KDevelop::ProjectBaseItem *item) con
return fileInformation(item).includes;
}
Path::List CMakeManager::frameworkDirectories(KDevelop::ProjectBaseItem *item) const
{
return fileInformation(item).frameworkDirectories;
}
QHash<QString, QString> CMakeManager::defines(KDevelop::ProjectBaseItem *item ) const
{
return fileInformation(item).defines;
......
......@@ -86,9 +86,10 @@ public:
Features features() const override { return Features(Folders | Targets | Files ); }
KDevelop::IProjectBuilder* builder() const override;
bool hasIncludesOrDefines(KDevelop::ProjectBaseItem*) const override;
bool hasBuildInfo(KDevelop::ProjectBaseItem*) const override;
KDevelop::Path buildDirectory(KDevelop::ProjectBaseItem*) const override;
KDevelop::Path::List includeDirectories(KDevelop::ProjectBaseItem *) const override;
KDevelop::Path::List frameworkDirectories(KDevelop::ProjectBaseItem *item) const override;
QHash<QString, QString> defines(KDevelop::ProjectBaseItem *) const override;
KDevelop::ProjectTargetItem* createTarget( const QString&, KDevelop::ProjectFolderItem* ) override { return 0; }
......
......@@ -36,11 +36,12 @@
struct CMakeFile
{
KDevelop::Path::List includes;
KDevelop::Path::List frameworkDirectories;
QHash<QString, QString> defines;
};
inline QDebug &operator<<(QDebug debug, const CMakeFile& file)
{
debug << "CMakeFile(-I" << file.includes << ", -D" << file.defines << ")";
debug << "CMakeFile(-I" << file.includes << ", -F" << file.frameworkDirectories << ", -D" << file.defines << ")";
return debug.maybeSpace();
}
......
......@@ -66,7 +66,7 @@ bool CustomBuildSystem::addFilesToTarget( const QList<ProjectFileItem*>&, Projec
return false;
}
bool CustomBuildSystem::hasIncludesOrDefines( ProjectBaseItem* ) const
bool CustomBuildSystem::hasBuildInfo( ProjectBaseItem* ) const
{
return false;
}
......@@ -144,6 +144,11 @@ Path::List CustomBuildSystem::includeDirectories( ProjectBaseItem* ) const
return {};
}
Path::List CustomBuildSystem::frameworkDirectories( ProjectBaseItem* ) const
{
return {};
}
KJob* CustomBuildSystem::install( KDevelop::ProjectBaseItem* item, const QUrl &installPrefix )
{
auto job = new CustomBuildJob( this, item, CustomBuildSystemTool::Install );
......
......@@ -70,12 +70,13 @@ public:
// BuildSystemManager API
public:
bool addFilesToTarget( const QList<KDevelop::ProjectFileItem*>& file, KDevelop::ProjectTargetItem* parent ) override;
bool hasIncludesOrDefines( KDevelop::ProjectBaseItem* ) const override;
bool hasBuildInfo( KDevelop::ProjectBaseItem* ) const override;
KDevelop::Path buildDirectory( KDevelop::ProjectBaseItem* ) const override;
IProjectBuilder* builder() const override;
KDevelop::ProjectTargetItem* createTarget( const QString& target, KDevelop::ProjectFolderItem* parent ) override;
QHash<QString, QString> defines( KDevelop::ProjectBaseItem* ) const override;
KDevelop::Path::List includeDirectories( KDevelop::ProjectBaseItem* ) const override;
KDevelop::Path::List frameworkDirectories( KDevelop::ProjectBaseItem* ) const override;
bool removeFilesFromTargets( const QList<KDevelop::ProjectFileItem*>& ) override;
bool removeTarget( KDevelop::ProjectTargetItem* target ) override;
QList<KDevelop::ProjectTargetItem*> targets( KDevelop::ProjectFolderItem* ) const override;
......
......@@ -62,7 +62,7 @@ public:
return {};
}
Path::List includesInBackground(const QString& path) const override
Path::List resolvePathInBackground(const QString& path, const bool isFrameworks) const
{
{
QReadLocker lock(&m_lock);
......@@ -77,7 +77,21 @@ public:
}
}
return m_resolver->resolveIncludePath(path).paths;
if (isFrameworks) {
return m_resolver->resolveIncludePath(path).frameworkDirectories;
} else {
return m_resolver->resolveIncludePath(path).paths;
}
}
Path::List includesInBackground(const QString& path) const override
{
return resolvePathInBackground(path, false);
}
Path::List frameworkDirectoriesInBackground(const QString& path) const override
{
return resolvePathInBackground(path, true);
}
IDefinesAndIncludesManager::Type type() const override
......@@ -137,6 +151,11 @@ Path::List CustomMakeManager::includeDirectories(KDevelop::ProjectBaseItem*) con
return Path::List();
}
Path::List CustomMakeManager::frameworkDirectories(KDevelop::ProjectBaseItem*) const
{
return Path::List();
}
QHash<QString,QString> CustomMakeManager::defines(KDevelop::ProjectBaseItem*) const
{
return QHash<QString,QString>();
......@@ -168,7 +187,7 @@ bool CustomMakeManager::removeFilesFromTargets(const QList< ProjectFileItem* > &
return false;
}
bool CustomMakeManager::hasIncludesOrDefines(KDevelop::ProjectBaseItem* item) const
bool CustomMakeManager::hasBuildInfo(KDevelop::ProjectBaseItem* item) const
{
Q_UNUSED(item);
return false;
......
......@@ -45,6 +45,11 @@ public:
*/
KDevelop::Path::List includeDirectories(KDevelop::ProjectBaseItem*) const override;
/**
* Provide a list of framework directories.
*/
KDevelop::Path::List frameworkDirectories(KDevelop::ProjectBaseItem*) const override;
/**
* Provide a list of files that contain the preprocessor defines for the
* project
......@@ -88,7 +93,7 @@ public:
/**
* Test if @p item has any includes or defines from this BSM
*/
bool hasIncludesOrDefines(KDevelop::ProjectBaseItem* item) const override;
bool hasBuildInfo(KDevelop::ProjectBaseItem* item) const override;
/**
* Get the toplevel build directory for the project
......
......@@ -66,6 +66,7 @@ namespace {
{ }
ModificationRevisionSet modificationTime;
Path::List paths;
Path::List frameworkDirectories;
QHash<QString, QString> defines;
QString errorMessage, longErrorMessage;
bool failed;
......@@ -248,12 +249,18 @@ namespace {
bool m_shouldTouchFiles;
};
void PathResolutionResult::mergeWith(const PathResolutionResult& rhs)
static void mergePaths(KDevelop::Path::List& destList, const KDevelop::Path::List& srcList)
{
foreach(const Path& path, rhs.paths) {
if(!paths.contains(path))
paths.append(path);
foreach (const Path& path, srcList) {
if (!destList.contains(path))
destList.append(path);
}
}
void PathResolutionResult::mergeWith(const PathResolutionResult& rhs)
{
mergePaths(paths, rhs.paths);
mergePaths(frameworkDirectories, rhs.frameworkDirectories);
includePathDependency += rhs.includePathDependency;
defines.unite(rhs.defines);
}
......@@ -397,7 +404,7 @@ PathResolutionResult MakeFileResolver::resolveIncludePath(const QString& file, c
}
}
if (!resultOnFail.errorMessage.isEmpty() || !resultOnFail.paths.isEmpty())
if (!resultOnFail.errorMessage.isEmpty() || !resultOnFail.paths.isEmpty() || !resultOnFail.frameworkDirectories.isEmpty())
return resultOnFail;
else
return PathResolutionResult(false, i18n("Makefile is missing in folder \"%1\"", dir.absolutePath()), i18n("Problem while trying to resolve include paths for %1", file));
......@@ -406,6 +413,7 @@ PathResolutionResult MakeFileResolver::resolveIncludePath(const QString& file, c
PushValue<bool> e(m_isResolving, true);
Path::List cachedPaths; //If the call doesn't succeed, use the cached not up-to-date version
Path::List cachedFWDirs;
QHash<QString, QString> cachedDefines;
ModificationRevisionSet dependency;
dependency.addModificationRevision(IndexedString(makeFile.filePath()), ModificationRevision::revisionForFile(IndexedString(makeFile.filePath())));
......@@ -416,12 +424,14 @@ PathResolutionResult MakeFileResolver::resolveIncludePath(const QString& file, c
it = s_cache.find(dir.path());
if (it != s_cache.end()) {
cachedPaths = it->paths;
cachedFWDirs = it->frameworkDirectories;
cachedDefines = it->defines;
if (dependency == it->modificationTime) {
if (!it->failed) {
//We have a valid cached result
PathResolutionResult ret(true);
ret.paths = it->paths;
ret.frameworkDirectories = it->frameworkDirectories;
ret.defines = it->defines;
ret.mergeWith(resultOnFail);
return ret;
......@@ -432,6 +442,7 @@ PathResolutionResult MakeFileResolver::resolveIncludePath(const QString& file, c
ret.errorMessage = i18n("Cached: %1", it->errorMessage);
ret.longErrorMessage = it->longErrorMessage;
ret.paths = it->paths;