Commit 1c4dbdc7 authored by Sergey Kalinichev's avatar Sergey Kalinichev
Browse files

Make the language standard per file.

REVIEW: 121777
parent a7526417
......@@ -27,6 +27,12 @@
#include "gcclikecompiler.h"
#include "msvccompiler.h"
namespace
{
const QString cpp11 = QStringLiteral("c++11");
const QString c99 = QStringLiteral("c99");
}
QString ClangFactory::name() const
{
return QStringLiteral("Clang");
......@@ -40,15 +46,16 @@ CompilerPointer ClangFactory::createCompiler(const QString& name, const QString&
void ClangFactory::registerDefaultCompilers(CompilerProvider* provider) const
{
const QString clang = QStringLiteral("clang");
if (GccLikeCompiler::supportedStandards(clang).contains("c++11")) {
auto compiler = createCompiler("Clang c++11", clang, false);
compiler->setLanguageStandard("c++11");
const auto standards = GccLikeCompiler::supportedStandards(clang);
if (standards.contains(cpp11)) {
auto compiler = createCompiler(QStringLiteral("Clang c++11"), clang, false);
compiler->setLanguageStandard(cpp11);
provider->registerCompiler(compiler);
}
if (GccLikeCompiler::supportedStandards(clang).contains("c99")) {
auto compiler = createCompiler("Clang c99", clang, false);
compiler->setLanguageStandard("c99");
if (standards.contains(c99)) {
auto compiler = createCompiler(QStringLiteral("Clang c99"), clang, false);
compiler->setLanguageStandard(c99);
provider->registerCompiler(compiler);
}
}
......@@ -66,15 +73,16 @@ CompilerPointer GccFactory::createCompiler(const QString& name, const QString& p
void GccFactory::registerDefaultCompilers(CompilerProvider* provider) const
{
const QString gcc = QStringLiteral("gcc");
if (GccLikeCompiler::supportedStandards(gcc).contains("c++11")) {
auto compiler = createCompiler("GCC c++11", gcc, false);
compiler->setLanguageStandard("c++11");
const auto standards = GccLikeCompiler::supportedStandards(gcc);
if (standards.contains(cpp11)) {
auto compiler = createCompiler(QStringLiteral("GCC c++11"), gcc, false);
compiler->setLanguageStandard(cpp11);
provider->registerCompiler(compiler);
}
if (GccLikeCompiler::supportedStandards(gcc).contains("c99")) {
auto compiler = createCompiler("GCC c99", gcc, false);
compiler->setLanguageStandard("c99");
if (standards.contains(c99)) {
auto compiler = createCompiler(QStringLiteral("GCC c99"), gcc, false);
compiler->setLanguageStandard(c99);
provider->registerCompiler(compiler);
}
}
......@@ -91,5 +99,5 @@ CompilerPointer MsvcFactory::createCompiler(const QString& name, const QString&
void MsvcFactory::registerDefaultCompilers(CompilerProvider* provider) const
{
provider->registerCompiler(createCompiler("MSVC", "cl.exe", false));
provider->registerCompiler(createCompiler(QStringLiteral("MSVC"), QStringLiteral("cl.exe"), false));
}
......@@ -67,6 +67,12 @@ public:
virtual void clearCache() override
{}
};
static CompilerPointer createDummyCompiler()
{
static CompilerPointer compiler(new NoCompiler());
return compiler;
}
}
CompilerProvider::CompilerProvider( SettingsManager* settings, QObject* parent )
......@@ -91,30 +97,12 @@ CompilerProvider::CompilerProvider( SettingsManager* settings, QObject* parent )
}
#endif
registerCompiler(CompilerPointer(new NoCompiler()));
registerCompiler(createDummyCompiler());
retrieveUserDefinedCompilers();
connect( ICore::self()->projectController(), &IProjectController::projectClosed,
this, &CompilerProvider::projectClosed);
//Add a provider for files without project
addPoject( nullptr, checkCompilerExists({}));
}
CompilerProvider::~CompilerProvider() = default;
CompilerPointer CompilerProvider::compilerForItem(ProjectBaseItem* item) const
{
auto project = item ? item->project() : nullptr;
if(!m_projects.contains(project)){
const_cast<CompilerProvider*>(this)->addProject(item->project());
}
Q_ASSERT(m_projects.contains(project));
auto compiler = m_projects[project];
Q_ASSERT(compiler);
return compiler;
}
QHash<QString, QString> CompilerProvider::defines( ProjectBaseItem* item ) const
{
return compilerForItem(item)->defines();
......@@ -130,17 +118,6 @@ IDefinesAndIncludesManager::Type CompilerProvider::type() const
return IDefinesAndIncludesManager::CompilerSpecific;
}
void CompilerProvider::addPoject( IProject* project, const CompilerPointer& compiler )
{
Q_ASSERT(compiler);
m_projects[project] = compiler;
}
void CompilerProvider::removePoject( IProject* project )
{
m_projects.remove( project );
}
CompilerPointer CompilerProvider::checkCompilerExists( const CompilerPointer& compiler ) const
{
//This may happen for opened for the first time projects
......@@ -149,11 +126,10 @@ CompilerPointer CompilerProvider::checkCompilerExists( const CompilerPointer& co
if ( QStandardPaths::findExecutable( compiler->path() ).isEmpty() ) {
continue;
}
definesAndIncludesDebug() << "Selected compiler: " << compiler->name();
return compiler;
}
qWarning() << "No compiler found. Standard includes/defines won't be provided to the project parser!";
}else{
} else {
for ( auto it = m_compilers.constBegin(); it != m_compilers.constEnd(); it++ ) {
if ( (*it)->name() == compiler->name() ) {
return *it;
......@@ -161,51 +137,47 @@ CompilerPointer CompilerProvider::checkCompilerExists( const CompilerPointer& co
}
}
return CompilerPointer(new NoCompiler());
return createDummyCompiler();
}
void CompilerProvider::setCompiler( IProject* project, const CompilerPointer& compiler )
QVector< CompilerPointer > CompilerProvider::compilers() const
{
auto c = checkCompilerExists( compiler );
Q_ASSERT(c);
addPoject( project, c );
return m_compilers;
}
void CompilerProvider::addProject( KDevelop::IProject* project )
CompilerPointer CompilerProvider::compilerForItem( KDevelop::ProjectBaseItem* item ) const
{
definesAndIncludesDebug() << "Adding project: " << project->name();
auto projectConfig = project->projectConfiguration().data();
if (!item) {
return checkCompilerExists({});
}
auto compiler = m_settings->currentCompiler( projectConfig, CompilerPointer(new NoCompiler()) );
auto name = compiler ? compiler->name() : QString();
compiler = checkCompilerExists( compiler );
const Path itemPath = item->path();
const Path rootDirectory = item->project()->path();
if ( compiler && ( compiler->name() != name ) ) {
m_settings->writeCurrentCompiler(projectConfig, compiler);
}
definesAndIncludesDebug() << "compiler is:" << compiler->name() << "standard:" << compiler->languageStandard();
auto compilers = m_settings->readPaths(item->project()->projectConfiguration().data());
CompilerPointer compiler;
Path closestPath;
addPoject( project, compiler );
}
// find compiler configured to a path closest to the requested item, or fallback to the default compiler
for (const auto& entry : compilers) {
auto compilerEntry = entry.compiler;
Path targetDirectory = rootDirectory;
void CompilerProvider::projectClosed( KDevelop::IProject* project )
{
removePoject( project );
}
targetDirectory.addPath(entry.path);
QVector< CompilerPointer > CompilerProvider::compilers() const
{
return m_compilers;
}
if (targetDirectory == itemPath){
return compilerEntry;
}
CompilerPointer CompilerProvider::currentCompiler(IProject* project) const
{
if(!m_projects.contains(project)){
const_cast<CompilerProvider*>(this)->addProject(project);
if (targetDirectory.isParentOf(itemPath)) {
if(!compiler || targetDirectory.segments().size() > closestPath.segments().size()){
compiler = compilerEntry;
closestPath = targetDirectory;
}
}
}
Q_ASSERT(m_projects.contains(project));
return m_projects[project];
return checkCompilerExists(compiler);
}
bool CompilerProvider::registerCompiler(const CompilerPointer& compiler)
......@@ -229,13 +201,6 @@ void CompilerProvider::unregisterCompiler(const CompilerPointer& compiler)
return;
}
for (auto it = m_projects.constBegin(); it != m_projects.constEnd(); it++) {
if (it.value() == compiler) {
//Set empty compiler for opened projects that use the compiler that is being unregistered
setCompiler(it.key(), CompilerPointer(new NoCompiler()));
}
}
for (int i = 0; i < m_compilers.count(); i++) {
if (m_compilers[i]->name() == compiler->name()) {
m_compilers.remove(i);
......
......@@ -42,10 +42,8 @@ public:
KDevelop::Path::List includes( KDevelop::ProjectBaseItem* item ) const override;
KDevelop::IDefinesAndIncludesManager::Type type() const override;
/// @return current compiler for the @P project
CompilerPointer currentCompiler( KDevelop::IProject* project ) const;
/// Select the @p compiler that provides standard includes/defines for the @p project
void setCompiler( KDevelop::IProject* project, const CompilerPointer& compiler );
/// @return current compiler for the @p item
CompilerPointer compilerForItem( KDevelop::ProjectBaseItem* item ) const;
/// @return list of all available compilers
QVector<CompilerPointer> compilers() const;
......@@ -60,21 +58,13 @@ public:
/// @return All available factories
QVector<CompilerFactoryPointer> compilerFactories() const;
private:
CompilerPointer compilerForItem( KDevelop::ProjectBaseItem* item ) const;
/// Checks wheter the @p compiler exist, if so returns it. Otherwise returns default compiler
CompilerPointer checkCompilerExists( const CompilerPointer& compiler ) const;
void addPoject( KDevelop::IProject* project, const CompilerPointer& compiler );
void removePoject( KDevelop::IProject* project );
private Q_SLOTS:
void addProject( KDevelop::IProject* );
void projectClosed( KDevelop::IProject* );
void retrieveUserDefinedCompilers();
private:
//list of compilers for each projects
QHash<KDevelop::IProject*, CompilerPointer> m_projects;
QVector<CompilerPointer> m_compilers;
QVector<CompilerFactoryPointer> m_factories;
......
......@@ -55,10 +55,10 @@ enum class CompilerStandard
namespace
{
QMap<CompilerStandard, QString> languageStandards()
const QMap<CompilerStandard, QString> languageStandards()
{
//TODO: query compiler for supported standards instead
static QMap<CompilerStandard, QString> standards {
const static QMap<CompilerStandard, QString> standards {
{CompilerStandard::C90, "c90"},
{CompilerStandard::C99, "c99"},
{CompilerStandard::C11, "c11"},
......@@ -78,7 +78,7 @@ QString compilerStandardToString(CompilerStandard standard)
return languageStandards().value(standard);
}
Q_ASSERT(0);
Q_UNREACHABLE();
return "Invalid";
}
......
......@@ -28,6 +28,7 @@
#include <interfaces/iproject.h>
#include <interfaces/icore.h>
#include <interfaces/iplugincontroller.h>
#include <project/projectmodel.h>
#include "compilerprovider.h"
......@@ -53,6 +54,46 @@ const QString compilerStandardKey = QLatin1String( "Standard" );
namespace
{
CompilerPointer createCompilerFromConfig(KConfigGroup& cfg)
{
auto grp = cfg.group( ConfigConstants::definesAndIncludesGroup ).group("Compiler");
auto name = grp.readEntry( ConfigConstants::compilerNameKey, QString() );
if (name.isEmpty()) {
return SettingsManager::globalInstance()->provider()->checkCompilerExists({});
}
for (auto c : SettingsManager::globalInstance()->provider()->compilers()) {
if (c->name() == name) {
return c;
}
}
auto path = grp.readEntry( ConfigConstants::compilerPathKey, QString() );
auto type = grp.readEntry( ConfigConstants::compilerTypeKey, QString() );
auto cf = SettingsManager::globalInstance()->provider()->compilerFactories();
for (auto f : cf) {
if (f->name() == type) {
auto compiler = f->createCompiler(name, path, true);
compiler->setLanguageStandard(grp.readEntry(ConfigConstants::compilerStandardKey, QString()));
return compiler;
}
}
return SettingsManager::globalInstance()->provider()->checkCompilerExists({});
}
void writeCompilerToConfig(KConfigGroup& cfg, const CompilerPointer& compiler)
{
Q_ASSERT(compiler);
auto grp = cfg.group(ConfigConstants::definesAndIncludesGroup).group("Compiler");
grp.writeEntry(ConfigConstants::compilerNameKey, compiler->name());
grp.writeEntry(ConfigConstants::compilerPathKey, compiler->path());
grp.writeEntry(ConfigConstants::compilerTypeKey, compiler->factoryName());
grp.writeEntry(ConfigConstants::compilerStandardKey, compiler->languageStandard());
}
void doWriteSettings( KConfigGroup grp, const QList<ConfigEntry>& paths )
{
int pathIndex = 0;
......@@ -79,6 +120,8 @@ void doWriteSettings( KConfigGroup grp, const QList<ConfigEntry>& paths )
s << defines;
pathgrp.writeEntry( ConfigConstants::definesKey, tmp );
}
writeCompilerToConfig(pathgrp, path.compiler);
}
}
......@@ -109,6 +152,9 @@ QList<ConfigEntry> doReadSettings( KConfigGroup grp, bool remove = false )
s.setVersion( QDataStream::Qt_4_5 );
s >> path.includes;
}
path.compiler = createCompilerFromConfig(pathgrp);
if ( remove ) {
pathgrp.deleteGroup();
}
......@@ -213,50 +259,12 @@ QList<ConfigEntry> SettingsManager::readPaths( KConfig* cfg ) const
return doReadSettings( grp );
}
CompilerPointer SettingsManager::currentCompiler( KConfig* cfg, const CompilerPointer& defaultCompiler ) const
{
auto grp = cfg->group( ConfigConstants::definesAndIncludesGroup ).group("Compiler");
auto name = grp.readEntry( ConfigConstants::compilerNameKey, QString() );
if (name.isEmpty()) {
return {};
}
for (auto c : m_provider.compilers()) {
if (c->name() == name) {
return c;
}
}
auto path = grp.readEntry( ConfigConstants::compilerPathKey, QString() );
auto type = grp.readEntry( ConfigConstants::compilerTypeKey, QString() );
auto cf = m_provider.compilerFactories();
for (auto f : cf) {
if (f->name() == type) {
auto compiler = f->createCompiler(name, path, true);
compiler->setLanguageStandard(grp.readEntry(ConfigConstants::compilerStandardKey, QString()));
return compiler;
}
}
return defaultCompiler;
}
bool SettingsManager::needToReparseCurrentProject( KConfig* cfg ) const
{
auto grp = cfg->group( ConfigConstants::definesAndIncludesGroup );
return grp.readEntry( "reparse", true );
}
void SettingsManager::writeCurrentCompiler(KConfig* cfg, const CompilerPointer& compiler)
{
auto grp = cfg->group(ConfigConstants::definesAndIncludesGroup).group("Compiler");
grp.writeEntry(ConfigConstants::compilerNameKey, compiler->name());
grp.writeEntry(ConfigConstants::compilerPathKey, compiler->path());
grp.writeEntry(ConfigConstants::compilerTypeKey, compiler->factoryName());
grp.writeEntry(ConfigConstants::compilerStandardKey, compiler->languageStandard());
}
void SettingsManager::writeUserDefinedCompilers(const QVector< CompilerPointer >& compilers)
{
QVector< CompilerPointer > editableCompilers;
......
......@@ -31,6 +31,7 @@ class KConfig;
namespace KDevelop {
class IProject;
class ProjectBaseItem;
}
struct ConfigEntry
......@@ -38,6 +39,7 @@ struct ConfigEntry
QString path;
QStringList includes;
KDevelop::Defines defines;
CompilerPointer compiler;
ConfigEntry( const QString& path = QString() ) : path( path ) {}
......@@ -54,15 +56,6 @@ public:
QList<ConfigEntry> readPaths(KConfig* cfg) const;
void writePaths(KConfig* cfg, const QList<ConfigEntry>& paths);
/**
* @param defaultCompiler the compiler that will be returned, if the @c CompilerProvider
* doesn't have a factory to create the needed type of compiler.
*
* @return stored compiler or nullptr if the project is opened for the first time.
*/
CompilerPointer currentCompiler(KConfig* cfg, const CompilerPointer& defaultCompiler = {}) const;
void writeCurrentCompiler(KConfig* cfg, const CompilerPointer& compiler);
QVector<CompilerPointer> userDefinedCompilers() const;
void writeUserDefinedCompilers(const QVector<CompilerPointer>& compilers);
......@@ -74,6 +67,7 @@ public:
static SettingsManager* globalInstance();
private:
CompilerProvider m_provider;
static SettingsManager* s_globalInstance;
};
......
......@@ -58,36 +58,35 @@ void TestCompilerProvider::testRegisterCompiler()
QVERIFY(!provider->registerCompiler({}));
}
void TestCompilerProvider::testSetCompiler()
{
SettingsManager settings;
auto provider = settings.provider();
provider->setCompiler(nullptr, {});
QVERIFY(provider->currentCompiler(nullptr));
for (auto c : provider->compilers()) {
provider->setCompiler(nullptr, c);
QCOMPARE(provider->currentCompiler(nullptr), c);
}
}
void TestCompilerProvider::testCompilerIncludesAndDefines()
{
//TODO: add test with project items
SettingsManager settings;
auto provider = settings.provider();
for (auto c : provider->compilers()) {
if (!c->editable() && !c->path().isEmpty()) {
provider->setCompiler(nullptr, c);
QVERIFY(!c->defines().isEmpty());
QVERIFY(!c->includes().isEmpty());
QCOMPARE(provider->defines(nullptr), c->defines());
QCOMPARE(provider->includes(nullptr), c->includes());
if (!c->supportedStandards().isEmpty()) {
QVERIFY(c->setLanguageStandard(c->supportedStandards().first()));
QVERIFY(!c->setLanguageStandard("bar"));
}
}
}
QVERIFY(!provider->defines(nullptr).isEmpty());
QVERIFY(!provider->includes(nullptr).isEmpty());
auto compiler = provider->compilerForItem(nullptr);
QVERIFY(compiler);
QVERIFY(!compiler->defines().isEmpty());
QVERIFY(!compiler->includes().isEmpty());
}
void TestCompilerProvider::testStorageBackwardsCompatible()
{
//FIXME: test compiler too
return;
SettingsManager settings;
QTemporaryFile file;
QVERIFY(file.open());
......
......@@ -33,7 +33,6 @@ private slots:
void initTestCase();
void cleanupTestCase();
void testRegisterCompiler();
void testSetCompiler();
void testCompilerIncludesAndDefines();
void testStorageBackwardsCompatible();
};
......
......@@ -34,6 +34,7 @@
#include <interfaces/idocumentcontroller.h>
#include <interfaces/idocument.h>
#include <serialization/indexedstring.h>
#include <project/projectmodel.h>
#include "definesandincludesconfigpage.h"
......@@ -56,11 +57,10 @@ void DefinesAndIncludesConfigPage::loadFrom( KConfig* cfg )
configWidget->clear();
auto settings = SettingsManager::globalInstance();
configWidget->setPaths( settings->readPaths( cfg ) );
auto provider = settings->provider();
configWidget->setCompilers(provider->compilers());
configWidget->setCurrentCompiler(provider->currentCompiler(project())->name());
configWidget->setPaths( settings->readPaths( cfg ) );
}
void DefinesAndIncludesConfigPage::saveTo(KConfig* cfg, KDevelop::IProject*)
......@@ -70,8 +70,6 @@ void DefinesAndIncludesConfigPage::saveTo(KConfig* cfg, KDevelop::IProject*)
auto provider = settings->provider();
settings->writeUserDefinedCompilers(configWidget->compilers());
settings->writeCurrentCompiler(cfg, configWidget->currentCompiler());
provider->setCompiler(project(), settings->currentCompiler(cfg));
const auto& providerCompilers = provider->compilers();
const auto& widgetCompilers = configWidget->compilers();
......
......@@ -60,6 +60,9 @@ QVariant ProjectPathsModel::data( const QModelIndex& index, int role ) const
case FullUrlDataRole:
return QVariant::fromValue(QUrl::fromUserInput( sanitizePath( pathConfig.path, true, false ) ));
break;
case CompilerDataRole:
return QVariant::fromValue(pathConfig.compiler);
break;
default:
break;
}
......@@ -112,6 +115,9 @@ bool ProjectPathsModel::setData( const QModelIndex& index, const QVariant& value
case FullUrlDataRole:
pathConfig.path = sanitizeUrl( value.value<QUrl>() );
break;
case CompilerDataRole:
pathConfig.compiler = value.value<CompilerPointer>();
break;
default:
return false;
break;
......