diff --git a/src/lib/corelib/tools/msvcinfo.cpp b/src/lib/corelib/tools/msvcinfo.cpp index 973284b2b..20698d818 100644 --- a/src/lib/corelib/tools/msvcinfo.cpp +++ b/src/lib/corelib/tools/msvcinfo.cpp @@ -55,6 +55,7 @@ #include #include #include +#include #ifdef Q_OS_WIN #include @@ -318,13 +319,16 @@ static QString vswhereFilePath() } enum class ProductType { VisualStudio, BuildTools }; -static std::vector retrieveInstancesFromVSWhere( - ProductType productType, Logger &logger) + +// QBS-1712: We need to decode the `vswhere` output to the unicode from the local +// console codepage, see https://github.com/Microsoft/vswhere/wiki/Encoding. +static QByteArray getVSWhereJsonOutput(ProductType productType, Logger &logger) { - std::vector result; const QString cmd = vswhereFilePath(); - if (cmd.isEmpty()) - return result; + if (cmd.isEmpty()) { + logger.qbsWarning() << Tr::tr("The vswhere tool not found"); + return {}; + } QProcess vsWhere; QStringList args = productType == ProductType::VisualStudio ? QStringList({QStringLiteral("-all"), QStringLiteral("-legacy"), @@ -334,22 +338,52 @@ static std::vector retrieveInstancesFromVSWhere( args << QStringLiteral("-format") << QStringLiteral("json") << QStringLiteral("-utf8"); vsWhere.start(cmd, args); if (!vsWhere.waitForStarted(-1)) - return result; + return {}; if (!vsWhere.waitForFinished(-1)) { logger.qbsWarning() << Tr::tr("The vswhere tool failed to run").append(QLatin1String(": ")) .append(vsWhere.errorString()); - return result; + return {}; } if (vsWhere.exitCode() != 0) { const QString stdOut = QString::fromLocal8Bit(vsWhere.readAllStandardOutput()); const QString stdErr = QString::fromLocal8Bit(vsWhere.readAllStandardError()); logger.qbsWarning() << Tr::tr("The vswhere tool failed to run").append(QLatin1String(".\n")) .append(formatVswhereOutput(stdOut, stdErr)); - return result; + return {}; } + + // This output may contains a non-unicode strings, we need to decode it + // using a local console codepage. + const auto output = vsWhere.readAllStandardOutput(); + const QSettings codepageRegistry( + QStringLiteral("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet" + "\\Control\\Nls\\CodePage"), + QSettings::NativeFormat); + const auto codepage = codepageRegistry.value(QStringLiteral("OEMCP")).toString(); + if (codepage.isEmpty()) { + logger.qbsInfo() << Tr::tr("Unable to decode vswere output due to OEMCP " + "not found in registry"); + return output; + } + const auto codec = QTextCodec::codecForName(QLatin1String("CP%1").arg(codepage).toLatin1()); + if (!codec) { + logger.qbsInfo() << Tr::tr("Unable to decode vswere output due to text " + "codec not found for codepage: cp%1").arg(codepage); + return output; + } + + return codec->toUnicode(output).toUtf8(); +} + +static std::vector retrieveInstancesFromVSWhere( + ProductType productType, Logger &logger) +{ + const auto output = getVSWhereJsonOutput(productType, logger); + if (output.isEmpty()) + return {}; + std::vector result; QJsonParseError parseError{}; - QJsonDocument jsonOutput = QJsonDocument::fromJson(vsWhere.readAllStandardOutput(), - &parseError); + QJsonDocument jsonOutput = QJsonDocument::fromJson(output, &parseError); if (parseError.error != QJsonParseError::NoError) { logger.qbsWarning() << Tr::tr("The vswhere tool produced invalid JSON output: %1") .arg(parseError.errorString());