diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.cpp index f65a8a69317..dd3e59d2c4f 100644 --- a/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.cpp +++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.cpp @@ -47,7 +47,7 @@ ClangStaticAnalyzerConfigWidget::ClangStaticAnalyzerConfigWidget( chooser->setExpectedKind(Utils::PathChooser::ExistingCommand); chooser->setHistoryCompleter(QLatin1String("ClangStaticAnalyzer.ClangCommand.History")); chooser->setPromptDialogTitle(tr("Clang Command")); - const auto validator = [chooser](Utils::FancyLineEdit *edit, QString *errorMessage) { + const auto validator = [chooser, this](Utils::FancyLineEdit *edit, QString *errorMessage) { const QString currentFilePath = chooser->fileName().toString(); Utils::PathChooser pc; Utils::PathChooser *helperPathChooser; @@ -58,8 +58,17 @@ ClangStaticAnalyzerConfigWidget::ClangStaticAnalyzerConfigWidget( } else { helperPathChooser = chooser; } - return chooser->defaultValidationFunction()(helperPathChooser->lineEdit(), errorMessage) + + const bool isExecutableValid = + chooser->defaultValidationFunction()(helperPathChooser->lineEdit(), errorMessage) && isClangExecutableUsable(helperPathChooser->fileName().toString(), errorMessage); + + const ClangExecutableVersion detectedVersion = isExecutableValid + ? clangExecutableVersion(helperPathChooser->fileName().toString()) + : ClangExecutableVersion(); + updateDetectedVersionLabel(isExecutableValid, detectedVersion); + + return isExecutableValid; }; chooser->setValidationFunction(validator); bool clangExeIsSet; @@ -90,5 +99,29 @@ ClangStaticAnalyzerConfigWidget::~ClangStaticAnalyzerConfigWidget() delete m_ui; } +void ClangStaticAnalyzerConfigWidget::updateDetectedVersionLabel( + bool isExecutableValid, + const ClangExecutableVersion &providedVersion) +{ + QLabel &label = *m_ui->detectedVersionLabel; + + if (isExecutableValid) { + if (providedVersion.isValid()) { + if (providedVersion.isSupportedVersion()) { + label.setText(tr("Version: %1, supported.") + .arg(providedVersion.toString())); + } else { + label.setText(tr("Version: %1, unsupported (supported version is %2).") + .arg(providedVersion.toString()) + .arg(ClangExecutableVersion::supportedVersionAsString())); + } + } else { + label.setText(tr("Version: Could not determine version.")); + } + } else { + label.setText(tr("Version: Set valid executable first.")); + } +} + } // namespace Internal } // namespace ClangStaticAnalyzer diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.h index f410670e504..6cc4e39eaf8 100644 --- a/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.h +++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.h @@ -34,6 +34,8 @@ namespace Internal { namespace Ui { class ClangStaticAnalyzerConfigWidget; } +class ClangExecutableVersion; + class ClangStaticAnalyzerConfigWidget : public QWidget { Q_OBJECT @@ -43,6 +45,9 @@ public: QWidget *parent = 0); ~ClangStaticAnalyzerConfigWidget(); + void updateDetectedVersionLabel(bool executableIsValid, + const ClangExecutableVersion &providedVersion); + private: Ui::ClangStaticAnalyzerConfigWidget *m_ui; ClangStaticAnalyzerSettings *m_settings; diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.ui b/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.ui index 800733a3317..3f4d3762ecb 100644 --- a/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.ui +++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerconfigwidget.ui @@ -34,14 +34,21 @@ - + + + + TextLabel + + + + Simultaneous processes: - + diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp index c0587a50815..22b9f86702b 100644 --- a/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp +++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerruncontrol.cpp @@ -469,6 +469,28 @@ void ClangStaticAnalyzerRunControl::start() emit finished(); return; } + + // Check clang version + const ClangExecutableVersion version = clangExecutableVersion(executable); + if (!version.isValid()) { + const QString warningMessage + = tr("Clang Static Analyzer: Running with possibly unsupported version, " + "could not determine version from executable \"%1\".") + .arg(executable); + appendMessage(warningMessage + QLatin1Char('\n'), Utils::StdErrFormat); + TaskHub::addTask(Task::Warning, warningMessage, Debugger::Constants::ANALYZERTASK_ID); + TaskHub::requestPopup(); + } else if (!version.isSupportedVersion()) { + const QString warningMessage + = tr("Clang Static Analyzer: Running with unsupported version %1, " + "supported version is %2.") + .arg(version.toString()) + .arg(ClangExecutableVersion::supportedVersionAsString()); + appendMessage(warningMessage + QLatin1Char('\n'), Utils::StdErrFormat); + TaskHub::addTask(Task::Warning, warningMessage, Debugger::Constants::ANALYZERTASK_ID); + TaskHub::requestPopup(); + } + m_clangExecutable = executable; // Create log dir diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerutils.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerutils.cpp index 0b7bc0f2308..a1a1bee67a4 100644 --- a/src/plugins/clangstaticanalyzer/clangstaticanalyzerutils.cpp +++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerutils.cpp @@ -32,9 +32,11 @@ #include #include +#include #include #include +#include static bool isFileExecutable(const QString &executablePath) { @@ -108,5 +110,53 @@ bool isClangExecutableUsable(const QString &filePath, QString *errorMessage) return true; } +ClangExecutableVersion clangExecutableVersion(const QString &executable) +{ + const ClangExecutableVersion invalidVersion; + + // Sanity checks + const QFileInfo fileInfo(executable); + const bool isExecutableFile = fileInfo.isFile() && fileInfo.isExecutable(); + if (!isExecutableFile) + return invalidVersion; + + // Get version output + Utils::Environment environment = Utils::Environment::systemEnvironment(); + Utils::Environment::setupEnglishOutput(&environment); + Utils::SynchronousProcess runner; + runner.setEnvironment(environment.toStringList()); + runner.setTimeoutS(10); + // We would prefer "-dumpversion", but that one returns some old version number. + const QStringList arguments(QLatin1String(("--version"))); + const Utils::SynchronousProcessResponse response = runner.runBlocking(executable, arguments); + if (response.result != Utils::SynchronousProcessResponse::Finished) + return invalidVersion; + const QString output = response.stdOut(); + + // Parse version output + const QRegularExpression re(QLatin1String("clang version (\\d+)\\.(\\d+)\\.(\\d+)")); + const QRegularExpressionMatch reMatch = re.match(output); + if (re.captureCount() != 3) + return invalidVersion; + + const QString majorString = reMatch.captured(1); + bool convertedSuccessfully = false; + const int major = majorString.toInt(&convertedSuccessfully); + if (!convertedSuccessfully) + return invalidVersion; + + const QString minorString = reMatch.captured(2); + const int minor = minorString.toInt(&convertedSuccessfully); + if (!convertedSuccessfully) + return invalidVersion; + + const QString patchString = reMatch.captured(3); + const int patch = patchString.toInt(&convertedSuccessfully); + if (!convertedSuccessfully) + return invalidVersion; + + return ClangExecutableVersion(major, minor, patch); +} + } // namespace Internal } // namespace ClangStaticAnalyzer diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerutils.h b/src/plugins/clangstaticanalyzer/clangstaticanalyzerutils.h index 6b0880fc67a..fb86bc1f942 100644 --- a/src/plugins/clangstaticanalyzer/clangstaticanalyzerutils.h +++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerutils.h @@ -44,5 +44,40 @@ QString clangExecutableFromSettings(Core::Id toolchainType, bool *isValid); QString createFullLocationString(const Debugger::DiagnosticLocation &location); +// TODO: Use QVersionNumber once we can use >= Qt 5.6.0 +class ClangExecutableVersion { +public: + ClangExecutableVersion() : majorNumber(-1) , minorNumber(-1) , patchNumber(-1) {} + ClangExecutableVersion(int major, int minor, int patch) + : majorNumber(major) , minorNumber(minor) , patchNumber(patch) {} + + bool isValid() const + { + return majorNumber >= 0 && minorNumber >= 0 && patchNumber >= 0; + } + + bool isSupportedVersion() const + { + return majorNumber == 3 && minorNumber == 8; + } + + static QString supportedVersionAsString() + { + return QLatin1String("3.8"); + } + + QString toString() const + { + return QString::fromLatin1("%1.%2.%3").arg(majorNumber).arg(minorNumber).arg(patchNumber); + } + +public: + int majorNumber; + int minorNumber; + int patchNumber; +}; + +ClangExecutableVersion clangExecutableVersion(const QString &absolutePath); + } // namespace Internal } // namespace ClangStaticAnalyzer