From 0ea3346d68890beed136bdce67ecf9cf0b4bb3b3 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Thu, 13 Dec 2018 08:57:58 +0100 Subject: [PATCH] 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 Reviewed-by: David Schulz Reviewed-by: Kai Koehne --- src/plugins/projectexplorer/msvctoolchain.cpp | 90 ++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index 7f58378e7fe..d221dd9710f 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -48,6 +48,8 @@ #include #include +#include +#include #include #include #include @@ -230,7 +232,79 @@ static Utils::optional detectCppBuildTools2017() return installation; } -static QVector detectVisualStudio() +static QVector detectVisualStudioFromVsWhere(const QString &vswhere) +{ + QVector 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 installation + = installationFromPathAndVersion(installationPath, version); + + if (installation) + installations.append(*installation); + } + return installations; +} + +static QVector detectVisualStudioFromRegistry() { QVector result; #ifdef Q_OS_WIN64 @@ -260,6 +334,20 @@ static QVector detectVisualStudio() return result; } +static QVector detectVisualStudio() +{ + const QString vswhere = windowsProgramFilesDir() + + "/Microsoft Visual Studio/Installer/vswhere.exe"; + if (QFileInfo::exists(vswhere)) { + const QVector installations + = detectVisualStudioFromVsWhere(vswhere); + if (!installations.isEmpty()) + return installations; + } + + return detectVisualStudioFromRegistry(); +} + static Abi findAbiOfMsvc(MsvcToolChain::Type type, MsvcToolChain::Platform platform, const QString &version) { Abi::Architecture arch = Abi::X86Architecture;