MsvcToolChain: Detect Visual Studio installations using vswhere

vswhere is Microsofts recommended way of detecting Visual Studio
installations. Starting with Visual Studio 2017 15.2 it is part of Visual
Studio's installation and according to the documentation can always be
found in %ProgramFiles(x86)%\Microsoft Visual Studio\Installer (see
https://github.com/Microsoft/vswhere).

Given the proper parameters, vswhere is able to find Visual Studio
installions that are older than 2015 (-legacy) and also build tools
(-products *). If the executable is not found, we fall back to the
registry search we did before, but we should use the tool provided
by MS for getting the job of detecting VS installations done.

Change-Id: Ia94e19f00a97138a0261f485c661673e821b06b5
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Reviewed-by: David Schulz <david.schulz@qt.io>
Reviewed-by: Kai Koehne <kai.koehne@qt.io>
This commit is contained in:
Oliver Wolff
2018-12-13 08:57:58 +01:00
parent a376eb0a81
commit 0ea3346d68

View File

@@ -48,6 +48,8 @@
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument>
#include <QProcess> #include <QProcess>
#include <QRegularExpression> #include <QRegularExpression>
#include <QSettings> #include <QSettings>
@@ -230,7 +232,79 @@ static Utils::optional<VisualStudioInstallation> detectCppBuildTools2017()
return installation; return installation;
} }
static QVector<VisualStudioInstallation> detectVisualStudio() static QVector<VisualStudioInstallation> detectVisualStudioFromVsWhere(const QString &vswhere)
{
QVector<VisualStudioInstallation> installations;
Utils::SynchronousProcess vsWhereProcess;
const int timeoutS = 5;
vsWhereProcess.setTimeoutS(timeoutS);
const QStringList arguments { "-products", "*", "-prerelease", "-legacy", "-format", "json",
"-utf8"};
Utils::SynchronousProcessResponse response = vsWhereProcess.run(vswhere, arguments);
switch (response.result) {
case Utils::SynchronousProcessResponse::Finished:
break;
case Utils::SynchronousProcessResponse::StartFailed:
qWarning().noquote() << QDir::toNativeSeparators(vswhere) << "could not be started.";
return installations;
case Utils::SynchronousProcessResponse::FinishedError:
qWarning().noquote().nospace() << QDir::toNativeSeparators(vswhere) << " finished with exit "
"code " << response.exitCode << ".";
return installations;
case Utils::SynchronousProcessResponse::TerminatedAbnormally:
qWarning().noquote().nospace() << QDir::toNativeSeparators(vswhere) << " crashed. Exit code: "
<< response.exitCode;
return installations;
case Utils::SynchronousProcessResponse::Hang:
qWarning().noquote() << QDir::toNativeSeparators(vswhere) << "did not finish in" << timeoutS
<< "seconds.";
return installations;
}
QByteArray output = response.stdOut().toUtf8();
QJsonParseError error;
const QJsonDocument doc = QJsonDocument::fromJson(output, &error);
if (error.error != QJsonParseError::NoError || doc.isNull()) {
qWarning() << "Could not parse json document from vswhere output.";
return installations;
}
const QJsonArray versions = doc.array();
if (versions.isEmpty()) {
qWarning() << "Could not detect any versions from vswhere output.";
return installations;
}
for (const QJsonValue vsVersion : versions) {
const QJsonObject vsVersionObj = vsVersion.toObject();
if (vsVersionObj.isEmpty()) {
qWarning() << "Could not obtain object from vswhere version";
continue;
}
QJsonValue value = vsVersionObj.value("installationVersion");
if (value.isUndefined()) {
qWarning() << "Could not obtain VS version from json output";
continue;
}
const QString versionString = value.toString();
QVersionNumber version = QVersionNumber::fromString(versionString);
value = vsVersionObj.value("installationPath");
if (value.isUndefined()) {
qWarning() << "Could not obtain VS installation path from json output";
continue;
}
const QString installationPath = value.toString();
Utils::optional<VisualStudioInstallation> installation
= installationFromPathAndVersion(installationPath, version);
if (installation)
installations.append(*installation);
}
return installations;
}
static QVector<VisualStudioInstallation> detectVisualStudioFromRegistry()
{ {
QVector<VisualStudioInstallation> result; QVector<VisualStudioInstallation> result;
#ifdef Q_OS_WIN64 #ifdef Q_OS_WIN64
@@ -260,6 +334,20 @@ static QVector<VisualStudioInstallation> detectVisualStudio()
return result; return result;
} }
static QVector<VisualStudioInstallation> detectVisualStudio()
{
const QString vswhere = windowsProgramFilesDir()
+ "/Microsoft Visual Studio/Installer/vswhere.exe";
if (QFileInfo::exists(vswhere)) {
const QVector<VisualStudioInstallation> installations
= detectVisualStudioFromVsWhere(vswhere);
if (!installations.isEmpty())
return installations;
}
return detectVisualStudioFromRegistry();
}
static Abi findAbiOfMsvc(MsvcToolChain::Type type, MsvcToolChain::Platform platform, const QString &version) static Abi findAbiOfMsvc(MsvcToolChain::Type type, MsvcToolChain::Platform platform, const QString &version)
{ {
Abi::Architecture arch = Abi::X86Architecture; Abi::Architecture arch = Abi::X86Architecture;