Commit a7526417 authored by Sergey Kalinichev's avatar Sergey Kalinichev
Browse files

Make it possible to change language standard for compiler

Now we can choose different language standards for deducing standard
include directories/defined macros. Also this feature is very useful
for the kdev-clang plugin. Now it can parse plain C projects.

REVIEW: 121777
parent a2bef3dd
......@@ -22,14 +22,14 @@
*/
#include "compilerfactories.h"
#include "compilerprovider.h"
#include "gcclikecompiler.h"
#include "msvccompiler.h"
QString ClangFactory::name() const
{
// TODO KF5: use QStringLiteral
return "Clang";
return QStringLiteral("Clang");
}
CompilerPointer ClangFactory::createCompiler(const QString& name, const QString& path, bool editable ) const
......@@ -37,10 +37,25 @@ CompilerPointer ClangFactory::createCompiler(const QString& name, const QString&
return CompilerPointer(new GccLikeCompiler(name, path, editable, this->name()));
}
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");
provider->registerCompiler(compiler);
}
if (GccLikeCompiler::supportedStandards(clang).contains("c99")) {
auto compiler = createCompiler("Clang c99", clang, false);
compiler->setLanguageStandard("c99");
provider->registerCompiler(compiler);
}
}
QString GccFactory::name() const
{
// TODO KF5: use QStringLiteral
return "GCC";
return QStringLiteral("GCC");
}
CompilerPointer GccFactory::createCompiler(const QString& name, const QString& path, bool editable ) const
......@@ -48,13 +63,33 @@ CompilerPointer GccFactory::createCompiler(const QString& name, const QString& p
return CompilerPointer(new GccLikeCompiler(name, path, editable, this->name()));
}
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");
provider->registerCompiler(compiler);
}
if (GccLikeCompiler::supportedStandards(gcc).contains("c99")) {
auto compiler = createCompiler("GCC c99", gcc, false);
compiler->setLanguageStandard("c99");
provider->registerCompiler(compiler);
}
}
QString MsvcFactory::name() const
{
// TODO KF5: use QStringLiteral
return "MSVC";
return QStringLiteral("MSVC");
}
CompilerPointer MsvcFactory::createCompiler(const QString& name, const QString& path, bool editable ) const
{
return CompilerPointer(new MsvcCompiler(name, path, editable, this->name()));
}
void MsvcFactory::registerDefaultCompilers(CompilerProvider* provider) const
{
provider->registerCompiler(createCompiler("MSVC", "cl.exe", false));
}
......@@ -32,6 +32,8 @@ public:
virtual CompilerPointer createCompiler( const QString& name, const QString& path, bool editable = true ) const override;
virtual QString name() const override;
virtual void registerDefaultCompilers(CompilerProvider* provider) const override;
};
class GccFactory : public ICompilerFactory
......@@ -40,6 +42,8 @@ public:
virtual CompilerPointer createCompiler( const QString& name, const QString& path, bool editable = true ) const override;
virtual QString name() const override;
virtual void registerDefaultCompilers(CompilerProvider* provider) const override;
};
class MsvcFactory : public ICompilerFactory
......@@ -48,6 +52,8 @@ public:
virtual CompilerPointer createCompiler( const QString& name, const QString& path, bool editable = true ) const override;
virtual QString name() const override;
virtual void registerDefaultCompilers(CompilerProvider* provider) const override;
};
#endif // COMPILERFACTORIES_H
......@@ -58,6 +58,14 @@ public:
{
return {};
}
virtual QStringList supportedStandards() const override
{
return {};
}
virtual void clearCache() override
{}
};
}
......@@ -72,14 +80,14 @@ CompilerProvider::CompilerProvider( SettingsManager* settings, QObject* parent )
#endif
if (!QStandardPaths::findExecutable( "gcc" ).isEmpty()) {
registerCompiler( m_factories[0]->createCompiler("GCC", "gcc", false) );
m_factories[0]->registerDefaultCompilers(this);
}
if (!QStandardPaths::findExecutable( "clang" ).isEmpty()) {
registerCompiler( m_factories[1]->createCompiler("Clang", "clang", false) );
m_factories[1]->registerDefaultCompilers(this);
}
#ifdef _WIN32
if (!QStandardPaths::findExecutable("cl.exe").isEmpty()) {
registerCompiler(m_factories[2]->createCompiler("MSVC", "cl.exe", false));
m_factories[2]->registerDefaultCompilers(this);
}
#endif
......@@ -176,7 +184,7 @@ void CompilerProvider::addProject( KDevelop::IProject* project )
if ( compiler && ( compiler->name() != name ) ) {
m_settings->writeCurrentCompiler(projectConfig, compiler);
}
definesAndIncludesDebug() << " compiler is: " << compiler->name();
definesAndIncludesDebug() << "compiler is:" << compiler->name() << "standard:" << compiler->languageStandard();
addPoject( project, compiler );
}
......
......@@ -26,6 +26,7 @@
#include <QDir>
#include <QProcess>
#include <QRegExp>
#include <QMap>
#include "../debugarea.h"
......@@ -37,6 +38,71 @@
using namespace KDevelop;
enum class CompilerStandard
{
C90 = 0,
C99,
C11,
CPP_98,
CPP_03,
CPP_11,
CPP_14,
DEFAULT = CPP_11
};
namespace
{
QMap<CompilerStandard, QString> languageStandards()
{
//TODO: query compiler for supported standards instead
static QMap<CompilerStandard, QString> standards {
{CompilerStandard::C90, "c90"},
{CompilerStandard::C99, "c99"},
{CompilerStandard::C11, "c11"},
{CompilerStandard::CPP_98, "c++98"},
{CompilerStandard::CPP_03, "c++03"},
{CompilerStandard::CPP_11, "c++11"},
{CompilerStandard::CPP_14, "c++14"},
};
return standards;
}
QString compilerStandardToString(CompilerStandard standard)
{
if(languageStandards().contains(standard)) {
return languageStandards().value(standard);
}
Q_ASSERT(0);
return "Invalid";
}
CompilerStandard stringToCompilerStandard(const QString& standard)
{
return languageStandards().key(standard, CompilerStandard::DEFAULT);
}
QString languageOption(CompilerStandard standard)
{
if (standard < CompilerStandard::CPP_98) {
return QStringLiteral("-xc");
}
return QStringLiteral("-xc++");
}
QString standardOption(CompilerStandard standard)
{
return QStringLiteral("-std=") + compilerStandardToString(standard);
}
}
Defines GccLikeCompiler::defines() const
{
if (!m_definesIncludes.definedMacros.isEmpty() ) {
......@@ -50,7 +116,9 @@ Defines GccLikeCompiler::defines() const
QProcess proc;
proc.setProcessChannelMode( QProcess::MergedChannels );
proc.start( path(), {"-std=c++11", "-xc++", "-dM", "-E", NULL_DEVICE} );
const auto standard = stringToCompilerStandard(languageStandard());
proc.start( path(), {standardOption(standard), languageOption(standard), "-dM", "-E", NULL_DEVICE} );
if ( !proc.waitForStarted( 1000 ) || !proc.waitForFinished( 1000 ) ) {
definesAndIncludesDebug() << "Unable to read standard macro definitions from "<< path();
return {};
......@@ -88,7 +156,9 @@ Path::List GccLikeCompiler::includes() const
// /usr/lib/gcc/i486-linux-gnu/4.1.2/include
// /usr/include
// End of search list.
proc.start( path(), {"-std=c++11", "-xc++", "-E", "-v", NULL_DEVICE} );
const auto standard = stringToCompilerStandard(languageStandard());
proc.start( path(), {standardOption(standard), languageOption(standard), "-E", "-v", NULL_DEVICE} );
if ( !proc.waitForStarted( 1000 ) || !proc.waitForFinished( 1000 ) ) {
definesAndIncludesDebug() << "Unable to read standard include paths from " << path();
return {};
......@@ -138,5 +208,20 @@ Path::List GccLikeCompiler::includes() const
GccLikeCompiler::GccLikeCompiler(const QString& name, const QString& path, bool editable, const QString& factoryName):
ICompiler(name, path, factoryName, editable)
{}
QStringList GccLikeCompiler::supportedStandards() const
{
return languageStandards().values();
}
void GccLikeCompiler::clearCache()
{
m_definesIncludes.includePaths.clear();
m_definesIncludes.definedMacros.clear();
}
QStringList GccLikeCompiler::supportedStandards(const QString& /*path*/)
{
return languageStandards().values();
}
......@@ -34,6 +34,22 @@ public:
virtual KDevelop::Defines defines() const override;
virtual KDevelop::Path::List includes() const override;
virtual QStringList supportedStandards() const override;
/// Helper function to retrieve compiler standards without creating a compiler
static QStringList supportedStandards(const QString& path);
protected:
virtual void clearCache();
private:
struct DefinesIncludes {
KDevelop::Defines definedMacros;
KDevelop::Path::List includePaths;
};
mutable DefinesIncludes m_definesIncludes;
};
#endif // GCCLIKECOMPILER_H
......
......@@ -23,6 +23,8 @@
#include "icompiler.h"
#include <QDebug>
using namespace KDevelop;
ICompiler::ICompiler(const QString& name, const QString& path, const QString& factoryName, bool editable):
......@@ -35,9 +37,8 @@ ICompiler::ICompiler(const QString& name, const QString& path, const QString& fa
void ICompiler::setPath(const QString& path)
{
if (editable()) {
m_definesIncludes.definedMacros.clear();
m_definesIncludes.includePaths.clear();
m_path = path;
clearCache();
}
}
......@@ -67,3 +68,19 @@ QString ICompiler::factoryName() const
{
return m_factoryName;
}
bool ICompiler::setLanguageStandard(const QString& standard)
{
if (supportedStandards().contains(standard)) {
m_standard = standard;
clearCache();
return true;
}
return false;
}
QString ICompiler::languageStandard() const
{
return m_standard;
}
......@@ -64,20 +64,33 @@ public:
/// @return name of the factory that created this compiler
QString factoryName() const;
/**
* sets language standard to @p standard
* @return true if compiler supports given standard
* @sa supportedStandards
*/
bool setLanguageStandard(const QString& standard);
/// @return current language standard
QString languageStandard() const;
/**
* @return list of all language standard, that current compiler supports
*/
virtual QStringList supportedStandards() const = 0;
virtual ~ICompiler() = default;
protected:
struct DefinesIncludes {
KDevelop::Defines definedMacros;
KDevelop::Path::List includePaths;
};
// list of defines/includes for the compiler. Use it for caching purposes
mutable DefinesIncludes m_definesIncludes;
protected :
/// Called when includes/defines cache must be cleared. E.g. when path to the compiler/standard changes
virtual void clearCache() = 0;
private:
bool m_editable;
QString m_name;
QString m_path;
QString m_factoryName;
QString m_standard;
};
typedef QSharedPointer<ICompiler> CompilerPointer;
......
......@@ -26,6 +26,8 @@
#include "icompiler.h"
class CompilerProvider;
/// Interface that represents a factory for creating compilers
class ICompilerFactory
{
......@@ -36,6 +38,12 @@ public:
///@see ICompiler
virtual CompilerPointer createCompiler( const QString& name, const QString& path, bool editable = true ) const = 0;
/**
* registers default compilers for the @p provider
* E.g. for gcc default compilers could be "gcc c99" and "gcc c++11"
*/
virtual void registerDefaultCompilers(CompilerProvider* provider) const = 0;
virtual ~ICompilerFactory() = default;
};
......
......@@ -128,3 +128,12 @@ Path::List MsvcCompiler::includes() const
MsvcCompiler::MsvcCompiler(const QString& name, const QString& path, bool editable, const QString& factoryName):
ICompiler(name, path, factoryName, editable)
{}
QStringList MsvcCompiler::supportedStandards() const
{
//TODO: query compiler for supported standards somehow
return {};
}
void MsvcCompiler::clearCache()
{}
\ No newline at end of file
......@@ -34,6 +34,11 @@ public:
virtual KDevelop::Defines defines() const override;
virtual KDevelop::Path::List includes() const override;
virtual QStringList supportedStandards() const override;
protected:
virtual void clearCache() override;
};
#endif // MSVCCOMPILER_H
......@@ -48,6 +48,7 @@ const QString compilersGroup = QLatin1String( "Compilers" );
const QString compilerNameKey = QLatin1String( "Name" );
const QString compilerPathKey = QLatin1String( "Path" );
const QString compilerTypeKey = QLatin1String( "Type" );
const QString compilerStandardKey = QLatin1String( "Standard" );
}
namespace
......@@ -232,7 +233,9 @@ CompilerPointer SettingsManager::currentCompiler( KConfig* cfg, const CompilerPo
auto cf = m_provider.compilerFactories();
for (auto f : cf) {
if (f->name() == type) {
return f->createCompiler(name, path, true);
auto compiler = f->createCompiler(name, path, true);
compiler->setLanguageStandard(grp.readEntry(ConfigConstants::compilerStandardKey, QString()));
return compiler;
}
}
......@@ -251,6 +254,7 @@ void SettingsManager::writeCurrentCompiler(KConfig* cfg, const CompilerPointer&
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)
......@@ -274,6 +278,7 @@ void SettingsManager::writeUserDefinedCompilers(const QVector< CompilerPointer >
grp.writeEntry(ConfigConstants::compilerNameKey, compiler->name());
grp.writeEntry(ConfigConstants::compilerPathKey, compiler->path());
grp.writeEntry(ConfigConstants::compilerTypeKey, compiler->factoryName());
grp.writeEntry(ConfigConstants::compilerStandardKey, compiler->languageStandard());
}
config.sync();
}
......@@ -294,7 +299,9 @@ QVector< CompilerPointer > SettingsManager::userDefinedCompilers() const
auto cf = m_provider.compilerFactories();
for (auto f : cf) {
if (f->name() == type) {
compilers.append(f->createCompiler(name, path));
auto compiler = f->createCompiler(name, path);
compiler->setLanguageStandard(grp.readEntry(ConfigConstants::compilerStandardKey, QString()));
compilers.append(compiler);
}
}
}
......
......@@ -76,6 +76,7 @@ CompilersWidget::CompilersWidget(QWidget* parent)
connect(m_ui->compilerName, &QLineEdit::editingFinished, this, &CompilersWidget::compilerEdited);
connect(m_ui->compilerPath, &QLineEdit::editingFinished, this, &CompilersWidget::compilerEdited);
connect(m_ui->languageStandard, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &CompilersWidget::compilerEdited);
connect(m_ui->compilerSelector, &QPushButton::clicked, this, &CompilersWidget::selectCompilerPathDialog);
......@@ -136,8 +137,12 @@ void CompilersWidget::compilerSelected(const QModelIndex& index)
{
auto compiler = index.data(CompilersModel::CompilerDataRole);
if (compiler.value<CompilerPointer>()) {
m_ui->languageStandard->clear();
m_ui->languageStandard->addItems(compiler.value<CompilerPointer>()->supportedStandards());
m_ui->compilerName->setText(compiler.value<CompilerPointer>()->name());
m_ui->compilerPath->setText(compiler.value<CompilerPointer>()->path());
m_ui->languageStandard->setCurrentText(compiler.value<CompilerPointer>()->languageStandard());
enableItems(true);
} else {
enableItems(false);
......@@ -156,6 +161,7 @@ void CompilersWidget::compilerEdited()
compiler.value<CompilerPointer>()->setName(m_ui->compilerName->text());
compiler.value<CompilerPointer>()->setPath(m_ui->compilerPath->text());
compiler.value<CompilerPointer>()->setLanguageStandard(m_ui->languageStandard->currentText());
m_compilersModel->updateCompiler(m_ui->compilers->selectionModel()->selection());
}
......@@ -174,6 +180,7 @@ void CompilersWidget::enableItems(bool enable)
{
m_ui->compilerName->setEnabled(enable);
m_ui->compilerPath->setEnabled(enable);
m_ui->languageStandard->setEnabled(enable);
m_ui->compilerSelector->setEnabled(enable);
if(!enable) {
......
......@@ -6,14 +6,11 @@
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
<width>424</width>
<height>336</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="margin">
<number>0</number>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QTreeView" name="compilers"/>
</item>
......@@ -49,47 +46,49 @@
</layout>
</item>
<item row="1" column="0">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string> Name</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="compilerName"/>
</item>
</layout>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string> Name</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Compiler path</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="compilerPath"/>
</item>
<item>
<widget class="QPushButton" name="compilerSelector">
<property name="maximumSize">
<size>
<width>30</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
<item row="0" column="1">
<widget class="QLineEdit" name="compilerName"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">