Commit eb4d1584 authored by Friedemann Kleint's avatar Friedemann Kleint

Add experimental clang-cl based toolchain.

Add new ClangClToolChain, which is autodetect by checking the registry
key created by LLVM and trying to find a suitable version of MSVC2015.

Add ClangClParser for parsing the output.

[ChangeLog][ProjectExplorer] Added experimental support for clang-cl.

Task-number: QTBUG-50804
Task-number: QTCREATORBUG-15641
Change-Id: Id34341570e935afc8ef6a104421d4abb5892176f
Reviewed-by: default avatarTobias Hunger <tobias.hunger@theqtcompany.com>
parent d3c23c33
......@@ -46,8 +46,12 @@ static QPair<Utils::FileName, int> parseFileName(const QString &input)
if (fileName.endsWith(QLatin1Char(')'))) {
int pos = fileName.lastIndexOf(QLatin1Char('('));
if (pos >= 0) {
// clang-cl gives column, too: "foo.cpp(34,1)" as opposed to MSVC "foo.cpp(34)".
int endPos = fileName.indexOf(QLatin1Char(','), pos + 1);
if (endPos < 0)
endPos = fileName.size() - 1;
bool ok = false;
int n = fileName.midRef(pos + 1, fileName.count() - pos - 2).toInt(&ok);
const int n = fileName.midRef(pos + 1, endPos - pos - 1).toInt(&ok);
if (ok) {
fileName = fileName.left(pos);
linenumber = n;
......@@ -191,6 +195,101 @@ void MsvcParser::doFlush()
emit addTask(t, m_lines, 1);
}
// --------------------------------------------------------------------------
// ClangClParser: The compiler errors look similar to MSVC, except that the
// column number is also given and there are no 4digit CXXXX error numbers.
// They are output to stderr.
// --------------------------------------------------------------------------
// ".\qwindowsgdinativeinterface.cpp(48,3) : error: unknown type name 'errr'"
static inline QString clangClCompilePattern()
{
return QLatin1Char('^') + QLatin1String(FILE_POS_PATTERN)
+ QLatin1String(" (warning|error): (")
+ QLatin1String(".*)$");
}
ClangClParser::ClangClParser()
: m_compileRegExp(clangClCompilePattern())
{
setObjectName(QLatin1String("ClangClParser"));
QTC_CHECK(m_compileRegExp.isValid());
}
void ClangClParser::stdOutput(const QString &line)
{
if (handleNmakeJomMessage(line, &m_lastTask)) {
m_linkedLines = 1;
doFlush();
return;
}
IOutputParser::stdOutput(line);
}
// Check for a code marker '~~~~ ^ ~~~~~~~~~~~~' underlining above code.
static inline bool isClangCodeMarker(const QString &trimmedLine)
{
return trimmedLine.constEnd() ==
std::find_if(trimmedLine.constBegin(), trimmedLine.constEnd(),
[] (QChar c) { return c != QLatin1Char(' ') && c != QLatin1Char('^') && c != QLatin1Char('~'); });
}
void ClangClParser::stdError(const QString &lineIn)
{
const QString line = IOutputParser::rightTrimmed(lineIn); // Strip \r\n.
if (handleNmakeJomMessage(line, &m_lastTask)) {
m_linkedLines = 1;
doFlush();
return;
}
// Finish a sequence of warnings/errors: "2 warnings generated."
if (!line.isEmpty() && line.at(0).isDigit() && line.endsWith(QLatin1String("generated."))) {
doFlush();
return;
}
// Start a new error message by a sequence of "In file included from " which is to be skipped.
if (line.startsWith(QLatin1String("In file included from "))) {
doFlush();
return;
}
QRegularExpressionMatch match = m_compileRegExp.match(line);
if (match.hasMatch()) {
doFlush();
const QPair<Utils::FileName, int> position = parseFileName(match.captured(1));
m_lastTask = Task(taskType(match.captured(2)), match.captured(3).trimmed(),
position.first, position.second,
Constants::TASK_CATEGORY_COMPILE);
m_linkedLines = 1;
return;
}
if (!m_lastTask.isNull()) {
const QString trimmed = line.trimmed();
if (isClangCodeMarker(trimmed)) {
doFlush();
return;
}
m_lastTask.description.append(QLatin1Char('\n'));
m_lastTask.description.append(trimmed);
++m_linkedLines;
return;
}
IOutputParser::stdError(lineIn);
}
void ClangClParser::doFlush()
{
if (!m_lastTask.isNull()) {
emit addTask(m_lastTask, m_linkedLines, 1);
m_lastTask.clear();
}
}
// Unit tests:
#ifdef WITH_TESTS
......@@ -423,4 +522,93 @@ void ProjectExplorerPlugin::testMsvcOutputParsers()
outputLines);
}
void ProjectExplorerPlugin::testClangClOutputParsers_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<OutputParserTester::Channel>("inputChannel");
QTest::addColumn<QString>("childStdOutLines");
QTest::addColumn<QString>("childStdErrLines");
QTest::addColumn<QList<Task> >("tasks");
QTest::addColumn<QString>("outputLines");
const QString warning1 = QLatin1String(
"private field 'm_version' is not used [-Wunused-private-field]\n"
"const int m_version; //! majorVersion<<8 + minorVersion");
const QString warning2 = QLatin1String(
"unused variable 'formatTextPlainC' [-Wunused-const-variable]\n"
"static const char formatTextPlainC[] = \"text/plain\";");
const QString warning3 = QLatin1String(
"unused variable 'formatTextHtmlC' [-Wunused-const-variable]\n"
"static const char formatTextHtmlC[] = \"text/html\";");
const QString error1 = QLatin1String(
"unknown type name 'errr'\n"
" errr");
const QString expectedError1 = QLatin1String(
"unknown type name 'errr'\n"
"errr"); // Line 2 trimmed.
const QString error2 = QLatin1String(
"expected unqualified-id\n"
"void *QWindowsGdiNativeInterface::nativeResourceForBackingStore(const QByteArray &resource, QBackingStore *bs)");
const QString clangClCompilerLog = QLatin1String(
"In file included from .\\qwindowseglcontext.cpp:40:\n"
"./qwindowseglcontext.h(282,15) : warning: ") + warning1 + QLatin1String("\n"
"5 warnings generated.\n"
".\\qwindowsclipboard.cpp(60,19) : warning: ") + warning2 + QLatin1String("\n"
" ^\n"
".\\qwindowsclipboard.cpp(61,19) : warning: ") + warning3 + QLatin1String("\n"
" ^\n"
"2 warnings generated.\n"
".\\qwindowsgdinativeinterface.cpp(48,3) : error: ") + error1 + QLatin1String("\n"
" ^\n"
".\\qwindowsgdinativeinterface.cpp(51,1) : error: ") + error2 + QLatin1String("\n"
"^\n"
"2 errors generated.\n");
const QString ignoredStderr = QLatin1String(
"NMAKE : fatal error U1077: 'D:\\opt\\LLVM64_390\\bin\\clang-cl.EXE' : return code '0x1'\n"
"Stop.");
const QString input = clangClCompilerLog + ignoredStderr;
const QString expectedStderr = ignoredStderr + QLatin1Char('\n');
QTest::newRow("error")
<< input
<< OutputParserTester::STDERR
<< QString() << expectedStderr
<< (QList<Task>()
<< Task(Task::Warning, warning1,
Utils::FileName::fromUserInput(QLatin1String("./qwindowseglcontext.h")), 282,
Constants::TASK_CATEGORY_COMPILE)
<< Task(Task::Warning, warning2,
Utils::FileName::fromUserInput(QLatin1String(".\\qwindowsclipboard.cpp")), 60,
Constants::TASK_CATEGORY_COMPILE)
<< Task(Task::Warning, warning3,
Utils::FileName::fromUserInput(QLatin1String(".\\qwindowsclipboard.cpp")), 61,
Constants::TASK_CATEGORY_COMPILE)
<< Task(Task::Error, expectedError1,
Utils::FileName::fromUserInput(QLatin1String(".\\qwindowsgdinativeinterface.cpp")), 48,
Constants::TASK_CATEGORY_COMPILE)
<< Task(Task::Error, error2,
Utils::FileName::fromUserInput(QLatin1String(".\\qwindowsgdinativeinterface.cpp")), 51,
Constants::TASK_CATEGORY_COMPILE))
<< QString();
}
void ProjectExplorerPlugin::testClangClOutputParsers()
{
OutputParserTester testbench;
testbench.appendOutputParser(new ClangClParser);
QFETCH(QString, input);
QFETCH(OutputParserTester::Channel, inputChannel);
QFETCH(QList<Task>, tasks);
QFETCH(QString, childStdOutLines);
QFETCH(QString, childStdErrLines);
QFETCH(QString, outputLines);
testbench.testParsing(input, inputChannel,
tasks, childStdOutLines, childStdErrLines,
outputLines);
}
#endif // WITH_TEST
......@@ -54,4 +54,22 @@ private:
int m_lines = 0;
};
class PROJECTEXPLORER_EXPORT ClangClParser : public ProjectExplorer::IOutputParser
{
Q_OBJECT
public:
ClangClParser();
void stdOutput(const QString &line) override;
void stdError(const QString &line) override;
private:
void doFlush() override;
const QRegularExpression m_compileRegExp;
Task m_lastTask;
int m_linkedLines = 0;
};
} // namespace ProjectExplorer
......@@ -52,13 +52,14 @@ public:
amd64_arm
};
MsvcToolChain(const QString &name, const Abi &abi,
const QString &varsBat, const QString &varsBatArg, Detection d = ManualDetection);
explicit MsvcToolChain(const QString &name, const Abi &abi,
const QString &varsBat, const QString &varsBatArg,
Detection d = ManualDetection);
MsvcToolChain();
bool isValid() const override;
Utils::FileNameList suggestedMkspecList() const override;
static MsvcToolChain *readFromMap(const QVariantMap &data);
QString typeDisplayName() const override;
QVariantMap toMap() const override;
......@@ -73,16 +74,46 @@ public:
bool operator == (const ToolChain &) const override;
protected:
explicit MsvcToolChain(Core::Id typeId, const QString &name, const Abi &abi,
const QString &varsBat, const QString &varsBatArg,
Detection d);
explicit MsvcToolChain(Core::Id typeId);
Utils::Environment readEnvironmentSetting(Utils::Environment& env) const override;
QByteArray msvcPredefinedMacros(const QStringList cxxflags,
const Utils::Environment &env) const override;
private:
MsvcToolChain();
QString m_varsBatArg; // Argument
};
class ClangClToolChain : public MsvcToolChain
{
public:
explicit ClangClToolChain(const QString &name, const QString &llvmDir,
const Abi &abi,
const QString &varsBat, const QString &varsBatArg,
Detection d = ManualDetection);
ClangClToolChain();
bool isValid() const override;
QString typeDisplayName() const override;
QList<Utils::FileName> suggestedMkspecList() const override;
void addToEnvironment(Utils::Environment &env) const override;
Utils::FileName compilerCommand() const override { return m_compiler; }
IOutputParser *outputParser() const override;
ToolChain *clone() const override;
QVariantMap toMap() const override;
bool fromMap(const QVariantMap &data) override;
ToolChainConfigWidget *configurationWidget() override;
QString llvmDir() const { return m_llvmDir; }
private:
QString m_llvmDir;
Utils::FileName m_compiler;
};
// --------------------------------------------------------------------------
// MsvcToolChainFactory
// --------------------------------------------------------------------------
......@@ -97,8 +128,7 @@ public:
QList<ToolChain *> autoDetect(const QList<ToolChain *> &alreadyKnown) override;
bool canRestore(const QVariantMap &data) override;
ToolChain *restore(const QVariantMap &data) override
{ return MsvcToolChain::readFromMap(data); }
ToolChain *restore(const QVariantMap &data) override;
ToolChainConfigWidget *configurationWidget(ToolChain *);
static QString vcVarsBatFor(const QString &basePath, const QString &toolchainName);
......@@ -108,26 +138,60 @@ private:
};
// --------------------------------------------------------------------------
// MsvcToolChainConfigWidget
// MsvcBasedToolChainConfigWidget
// --------------------------------------------------------------------------
class MsvcToolChainConfigWidget : public ToolChainConfigWidget
class MsvcBasedToolChainConfigWidget : public ToolChainConfigWidget
{
Q_OBJECT
public:
MsvcToolChainConfigWidget(ToolChain *);
explicit MsvcBasedToolChainConfigWidget(ToolChain *);
private:
protected:
void applyImpl() override { }
void discardImpl() override { setFromToolChain(); }
void discardImpl() override { setFromMsvcToolChain(); }
bool isDirtyImpl() const override { return false; }
void makeReadOnlyImpl() override { }
void setFromToolChain();
void setFromMsvcToolChain();
private:
QLabel *m_nameDisplayLabel;
QLabel *m_varsBatDisplayLabel;
};
// --------------------------------------------------------------------------
// MsvcToolChainConfigWidget
// --------------------------------------------------------------------------
class MsvcToolChainConfigWidget : public MsvcBasedToolChainConfigWidget
{
Q_OBJECT
public:
explicit MsvcToolChainConfigWidget(ToolChain *);
};
// --------------------------------------------------------------------------
// ClangClToolChainConfigWidget
// --------------------------------------------------------------------------
class ClangClToolChainConfigWidget : public MsvcBasedToolChainConfigWidget
{
Q_OBJECT
public:
explicit ClangClToolChainConfigWidget(ToolChain *);
protected:
void discardImpl() override { setFromClangClToolChain(); }
private:
void setFromClangClToolChain();
QLabel *m_llvmDirLabel;
};
} // namespace Internal
} // namespace ProjectExplorer
......@@ -206,6 +206,9 @@ private slots:
void testMsvcOutputParsers_data();
void testMsvcOutputParsers();
void testClangClOutputParsers_data();
void testClangClOutputParsers();
void testGccAbiGuessing_data();
void testGccAbiGuessing();
......
......@@ -212,6 +212,7 @@ const char GCC_TOOLCHAIN_TYPEID[] = "ProjectExplorer.ToolChain.Gcc";
const char LINUXICC_TOOLCHAIN_TYPEID[] = "ProjectExplorer.ToolChain.LinuxIcc";
const char MINGW_TOOLCHAIN_TYPEID[] = "ProjectExplorer.ToolChain.Mingw";
const char MSVC_TOOLCHAIN_TYPEID[] = "ProjectExplorer.ToolChain.Msvc";
const char CLANG_CL_TOOLCHAIN_TYPEID[] = "ProjectExplorer.ToolChain.ClangCl";
const char WINCE_TOOLCHAIN_TYPEID[] = "ProjectExplorer.ToolChain.WinCE";
const char CUSTOM_TOOLCHAIN_TYPEID[] = "ProjectExplorer.ToolChain.Custom";
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment