Merge topic 'vs-instance'

195d47e213 VS: Allow CMAKE_GENERATOR_INSTANCE to specify portable instance
ec8d37b3b1 VS: Support version specification in CMAKE_GENERATOR_INSTANCE
8e6d930e8c VS: Parse comma-separated fields from CMAKE_GENERATOR_INSTANCE
5d1f377737 cmVSSetupHelper: Factor out helper to load MSVC toolset version
006fe1e919 cmVSSetupHelper: Convert wide to narrow strings early
f5dfc788b8 cmVSSetupHelper: Drop unused InstanceId field
3213e2595d cmVSSetupHelper: Drop unused ullVersion field
152f9978dd Help: De-duplicate VS instance selection documentation

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !6651
This commit is contained in:
Brad King
2021-11-01 20:24:36 +00:00
committed by Kitware Robot
35 changed files with 426 additions and 95 deletions
@@ -6,6 +6,7 @@
#include "cmsys/FStream.hxx"
#include "cmsys/Glob.hxx"
#include "cmsys/RegularExpression.hxx"
#include "cmAlgorithms.h"
#include "cmDocumentationEntry.h"
@@ -109,6 +110,30 @@ static const char* VSVersionToToolset(
return "";
}
static std::string VSVersionToMajorString(
cmGlobalVisualStudioGenerator::VSVersion v)
{
switch (v) {
case cmGlobalVisualStudioGenerator::VS9:
return "9";
case cmGlobalVisualStudioGenerator::VS10:
return "10";
case cmGlobalVisualStudioGenerator::VS11:
return "11";
case cmGlobalVisualStudioGenerator::VS12:
return "12";
case cmGlobalVisualStudioGenerator::VS14:
return "14";
case cmGlobalVisualStudioGenerator::VS15:
return "15";
case cmGlobalVisualStudioGenerator::VS16:
return "16";
case cmGlobalVisualStudioGenerator::VS17:
return "17";
}
return "";
}
static const char* VSVersionToAndroidToolset(
cmGlobalVisualStudioGenerator::VSVersion v)
{
@@ -441,8 +466,36 @@ bool cmGlobalVisualStudioVersionedGenerator::SetGeneratorInstance(
return true;
}
if (!this->ParseGeneratorInstance(i, mf)) {
return false;
}
if (!this->GeneratorInstanceVersion.empty()) {
std::string const majorStr = VSVersionToMajorString(this->Version);
cmsys::RegularExpression versionRegex(
cmStrCat("^", majorStr, "\\.[0-9]+\\.[0-9]+\\.[0-9]+$"));
if (!versionRegex.find(this->GeneratorInstanceVersion)) {
std::ostringstream e;
/* clang-format off */
e <<
"Generator\n"
" " << this->GetName() << "\n"
"given instance specification\n"
" " << i << "\n"
"but the version field is not 4 integer components"
" starting in " << majorStr << "."
;
/* clang-format on */
mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
return false;
}
}
std::string vsInstance;
if (!i.empty()) {
if (!this->vsSetupAPIHelper.SetVSInstance(i)) {
vsInstance = i;
if (!this->vsSetupAPIHelper.SetVSInstance(
this->GeneratorInstance, this->GeneratorInstanceVersion)) {
std::ostringstream e;
/* clang-format off */
e <<
@@ -451,13 +504,17 @@ bool cmGlobalVisualStudioVersionedGenerator::SetGeneratorInstance(
"could not find specified instance of Visual Studio:\n"
" " << i;
/* clang-format on */
if (!this->GeneratorInstance.empty() &&
this->GeneratorInstanceVersion.empty() &&
cmSystemTools::FileIsDirectory(this->GeneratorInstance)) {
e << "\n"
"The directory exists, but the instance is not known to the "
"Visual Studio Installer, and no 'version=' field was given.";
}
mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
return false;
}
}
std::string vsInstance;
if (!this->vsSetupAPIHelper.GetVSInstanceInfo(vsInstance)) {
} else if (!this->vsSetupAPIHelper.GetVSInstanceInfo(vsInstance)) {
std::ostringstream e;
/* clang-format off */
e <<
@@ -485,6 +542,88 @@ bool cmGlobalVisualStudioVersionedGenerator::SetGeneratorInstance(
return true;
}
bool cmGlobalVisualStudioVersionedGenerator::ParseGeneratorInstance(
std::string const& is, cmMakefile* mf)
{
this->GeneratorInstance.clear();
this->GeneratorInstanceVersion.clear();
std::vector<std::string> const fields = cmTokenize(is, ",");
std::vector<std::string>::const_iterator fi = fields.begin();
if (fi == fields.end()) {
return true;
}
// The first field may be the VS instance.
if (fi->find('=') == fi->npos) {
this->GeneratorInstance = *fi;
++fi;
}
std::set<std::string> handled;
// The rest of the fields must be key=value pairs.
for (; fi != fields.end(); ++fi) {
std::string::size_type pos = fi->find('=');
if (pos == fi->npos) {
std::ostringstream e;
/* clang-format off */
e <<
"Generator\n"
" " << this->GetName() << "\n"
"given instance specification\n"
" " << is << "\n"
"that contains a field after the first ',' with no '='."
;
/* clang-format on */
mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
return false;
}
std::string const key = fi->substr(0, pos);
std::string const value = fi->substr(pos + 1);
if (!handled.insert(key).second) {
std::ostringstream e;
/* clang-format off */
e <<
"Generator\n"
" " << this->GetName() << "\n"
"given instance specification\n"
" " << is << "\n"
"that contains duplicate field key '" << key << "'."
;
/* clang-format on */
mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
return false;
}
if (!this->ProcessGeneratorInstanceField(key, value)) {
std::ostringstream e;
/* clang-format off */
e <<
"Generator\n"
" " << this->GetName() << "\n"
"given instance specification\n"
" " << is << "\n"
"that contains invalid field '" << *fi << "'."
;
/* clang-format on */
mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
return false;
}
}
return true;
}
bool cmGlobalVisualStudioVersionedGenerator::ProcessGeneratorInstanceField(
std::string const& key, std::string const& value)
{
if (key == "version") {
this->GeneratorInstanceVersion = value;
return true;
}
return false;
}
bool cmGlobalVisualStudioVersionedGenerator::GetVSInstance(
std::string& dir) const
{
@@ -65,6 +65,9 @@ protected:
std::string GetWindows10SDKMaxVersionDefault(cmMakefile*) const override;
virtual bool ProcessGeneratorInstanceField(std::string const& key,
std::string const& value);
std::string FindMSBuildCommand() override;
std::string FindDevEnvCommand() override;
@@ -76,5 +79,10 @@ private:
class Factory17;
friend class Factory17;
mutable cmVSSetupAPIHelper vsSetupAPIHelper;
bool ParseGeneratorInstance(std::string const& is, cmMakefile* mf);
std::string GeneratorInstance;
std::string GeneratorInstanceVersion;
cm::optional<std::string> LastGeneratorInstanceString;
};
+87 -43
View File
@@ -2,6 +2,8 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmVSSetupHelper.h"
#include <utility>
#include "cmsys/Encoding.hxx"
#include "cmsys/FStream.hxx"
@@ -46,17 +48,36 @@ const CLSID CLSID_SetupConfiguration = {
/* clang-format on */
#endif
namespace {
const WCHAR* Win10SDKComponent =
L"Microsoft.VisualStudio.Component.Windows10SDK";
const WCHAR* Win81SDKComponent =
L"Microsoft.VisualStudio.Component.Windows81SDK";
const WCHAR* ComponentType = L"Component";
bool LoadVSInstanceVCToolsetVersion(VSInstanceInfo& vsInstanceInfo)
{
std::string const vcRoot = vsInstanceInfo.GetInstallLocation();
std::string vcToolsVersionFile =
vcRoot + "/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt";
std::string vcToolsVersion;
cmsys::ifstream fin(vcToolsVersionFile.c_str());
if (!fin || !cmSystemTools::GetLineFromStream(fin, vcToolsVersion)) {
return false;
}
vcToolsVersion = cmTrimWhitespace(vcToolsVersion);
std::string const vcToolsDir = vcRoot + "/VC/Tools/MSVC/" + vcToolsVersion;
if (!cmSystemTools::FileIsDirectory(vcToolsDir)) {
return false;
}
vsInstanceInfo.VCToolsetVersion = vcToolsVersion;
return true;
}
}
std::string VSInstanceInfo::GetInstallLocation() const
{
std::string loc = cmsys::Encoding::ToNarrow(this->VSInstallLocation);
cmSystemTools::ConvertToUnixSlashes(loc);
return loc;
return this->VSInstallLocation;
}
cmVSSetupAPIHelper::cmVSSetupAPIHelper(unsigned int version)
@@ -83,10 +104,12 @@ cmVSSetupAPIHelper::~cmVSSetupAPIHelper()
CoUninitialize();
}
bool cmVSSetupAPIHelper::SetVSInstance(std::string const& vsInstallLocation)
bool cmVSSetupAPIHelper::SetVSInstance(std::string const& vsInstallLocation,
std::string const& vsInstallVersion)
{
this->SpecifiedVSInstallLocation = vsInstallLocation;
cmSystemTools::ConvertToUnixSlashes(this->SpecifiedVSInstallLocation);
this->SpecifiedVSInstallVersion = vsInstallVersion;
chosenInstanceInfo = VSInstanceInfo();
return this->EnumerateAndChooseVSInstance();
}
@@ -152,29 +175,17 @@ bool cmVSSetupAPIHelper::GetVSInstanceInfo(
if (pInstance == NULL)
return false;
SmartBSTR bstrId;
if (SUCCEEDED(pInstance->GetInstanceId(&bstrId))) {
vsInstanceInfo.InstanceId = std::wstring(bstrId);
} else {
return false;
}
InstanceState state;
if (FAILED(pInstance->GetState(&state))) {
return false;
}
ULONGLONG ullVersion = 0;
SmartBSTR bstrVersion;
if (FAILED(pInstance->GetInstallationVersion(&bstrVersion))) {
return false;
} else {
vsInstanceInfo.Version = std::wstring(bstrVersion);
if (FAILED(setupHelper->ParseVersion(bstrVersion, &ullVersion))) {
vsInstanceInfo.ullVersion = 0;
} else {
vsInstanceInfo.ullVersion = ullVersion;
}
vsInstanceInfo.Version =
cmsys::Encoding::ToNarrow(std::wstring(bstrVersion));
}
// Reboot may have been required before the installation path was created.
@@ -183,26 +194,15 @@ bool cmVSSetupAPIHelper::GetVSInstanceInfo(
if (FAILED(pInstance->GetInstallationPath(&bstrInstallationPath))) {
return false;
} else {
vsInstanceInfo.VSInstallLocation = std::wstring(bstrInstallationPath);
vsInstanceInfo.VSInstallLocation =
cmsys::Encoding::ToNarrow(std::wstring(bstrInstallationPath));
cmSystemTools::ConvertToUnixSlashes(vsInstanceInfo.VSInstallLocation);
}
}
// Check if a compiler is installed with this instance.
{
std::string const vcRoot = vsInstanceInfo.GetInstallLocation();
std::string vcToolsVersionFile =
vcRoot + "/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt";
std::string vcToolsVersion;
cmsys::ifstream fin(vcToolsVersionFile.c_str());
if (!fin || !cmSystemTools::GetLineFromStream(fin, vcToolsVersion)) {
return false;
}
vcToolsVersion = cmTrimWhitespace(vcToolsVersion);
std::string const vcToolsDir = vcRoot + "/VC/Tools/MSVC/" + vcToolsVersion;
if (!cmSystemTools::FileIsDirectory(vcToolsDir)) {
return false;
}
vsInstanceInfo.VCToolsetVersion = vcToolsVersion;
if (!LoadVSInstanceVCToolsetVersion(vsInstanceInfo)) {
return false;
}
// Reboot may have been required before the product package was registered
@@ -264,7 +264,7 @@ bool cmVSSetupAPIHelper::GetVSInstanceVersion(std::string& vsInstanceVersion)
bool isInstalled = this->EnumerateAndChooseVSInstance();
if (isInstalled) {
vsInstanceVersion = cmsys::Encoding::ToNarrow(chosenInstanceInfo.Version);
vsInstanceVersion = chosenInstanceInfo.Version;
}
return isInstalled;
@@ -298,7 +298,7 @@ bool cmVSSetupAPIHelper::IsEWDKEnabled()
bool cmVSSetupAPIHelper::EnumerateAndChooseVSInstance()
{
bool isVSInstanceExists = false;
if (chosenInstanceInfo.VSInstallLocation.compare(L"") != 0) {
if (chosenInstanceInfo.VSInstallLocation.compare("") != 0) {
return true;
}
@@ -311,12 +311,11 @@ bool cmVSSetupAPIHelper::EnumerateAndChooseVSInstance()
if (envVSVersion.empty() || envVsInstallDir.empty())
return false;
chosenInstanceInfo.VSInstallLocation =
std::wstring(envVsInstallDir.begin(), envVsInstallDir.end());
chosenInstanceInfo.Version =
std::wstring(envVSVersion.begin(), envVSVersion.end());
chosenInstanceInfo.VCToolsetVersion = envVSVersion;
chosenInstanceInfo.ullVersion = std::stoi(envVSVersion);
chosenInstanceInfo.VSInstallLocation = envVsInstallDir;
chosenInstanceInfo.Version = envVSVersion;
if (!LoadVSInstanceVCToolsetVersion(chosenInstanceInfo)) {
return false;
}
chosenInstanceInfo.IsWin10SDKInstalled = true;
chosenInstanceInfo.IsWin81SDKInstalled = !envWindowsSdkDir81.empty();
return true;
@@ -343,7 +342,9 @@ bool cmVSSetupAPIHelper::EnumerateAndChooseVSInstance()
return false;
}
std::wstring const wantVersion = std::to_wstring(this->Version) + L'.';
std::string const wantVersion = std::to_string(this->Version) + '.';
bool specifiedLocationNotSpecifiedVersion = false;
SmartCOMPtr<ISetupInstance> instance;
while (SUCCEEDED(enumInstances->Next(1, &instance, NULL)) && instance) {
@@ -371,6 +372,16 @@ bool cmVSSetupAPIHelper::EnumerateAndChooseVSInstance()
std::string currentVSLocation = instanceInfo.GetInstallLocation();
if (cmSystemTools::ComparePath(currentVSLocation,
this->SpecifiedVSInstallLocation)) {
if (this->SpecifiedVSInstallVersion.empty() ||
instanceInfo.Version == this->SpecifiedVSInstallVersion) {
chosenInstanceInfo = instanceInfo;
return true;
}
specifiedLocationNotSpecifiedVersion = true;
}
} else if (!this->SpecifiedVSInstallVersion.empty()) {
// We are looking for a specific version.
if (instanceInfo.Version == this->SpecifiedVSInstallVersion) {
chosenInstanceInfo = instanceInfo;
return true;
}
@@ -392,6 +403,13 @@ bool cmVSSetupAPIHelper::EnumerateAndChooseVSInstance()
}
}
if (!this->SpecifiedVSInstallLocation.empty() &&
!specifiedLocationNotSpecifiedVersion) {
// The VS Installer does not know about the specified location.
// Check for one directly on disk.
return this->LoadSpecifiedVSInstanceFromDisk();
}
if (vecVSInstances.size() > 0) {
isVSInstanceExists = true;
int index = ChooseVSInstance(vecVSInstances);
@@ -454,6 +472,32 @@ int cmVSSetupAPIHelper::ChooseVSInstance(
return chosenIndex;
}
bool cmVSSetupAPIHelper::LoadSpecifiedVSInstanceFromDisk()
{
if (!cmSystemTools::FileIsDirectory(this->SpecifiedVSInstallLocation)) {
return false;
}
VSInstanceInfo vsInstanceInfo;
vsInstanceInfo.VSInstallLocation = this->SpecifiedVSInstallLocation;
// FIXME: Is there a better way to get SDK information?
vsInstanceInfo.IsWin10SDKInstalled = true;
vsInstanceInfo.IsWin81SDKInstalled = false;
if (!this->SpecifiedVSInstallVersion.empty()) {
// Assume the version specified by the user is correct.
vsInstanceInfo.Version = this->SpecifiedVSInstallVersion;
} else {
return false;
}
if (!LoadVSInstanceVCToolsetVersion(vsInstanceInfo)) {
return false;
}
chosenInstanceInfo = std::move(vsInstanceInfo);
return true;
}
bool cmVSSetupAPIHelper::Initialize()
{
if (initializationFailure)
+6 -5
View File
@@ -84,11 +84,9 @@ private:
struct VSInstanceInfo
{
std::wstring InstanceId;
std::wstring VSInstallLocation;
std::wstring Version;
std::string VSInstallLocation;
std::string Version;
std::string VCToolsetVersion;
ULONGLONG ullVersion = 0; // A.B.C.D = (A<<48)|(B<<32)|(C<<16)|D
bool IsWin10SDKInstalled = false;
bool IsWin81SDKInstalled = false;
@@ -101,7 +99,8 @@ public:
cmVSSetupAPIHelper(unsigned int version);
~cmVSSetupAPIHelper();
bool SetVSInstance(std::string const& vsInstallLocation);
bool SetVSInstance(std::string const& vsInstallLocation,
std::string const& vsInstallVersion);
bool IsVSInstalled();
bool GetVSInstanceInfo(std::string& vsInstallLocation);
@@ -118,6 +117,7 @@ private:
bool& bWin10SDK, bool& bWin81SDK);
int ChooseVSInstance(const std::vector<VSInstanceInfo>& vecVSInstances);
bool EnumerateAndChooseVSInstance();
bool LoadSpecifiedVSInstanceFromDisk();
unsigned int Version;
@@ -134,4 +134,5 @@ private:
bool IsEWDKEnabled();
std::string SpecifiedVSInstallLocation;
std::string SpecifiedVSInstallVersion;
};