mirror of
https://github.com/Kitware/CMake.git
synced 2026-04-26 00:00:39 -05:00
Merge topic 'vs-instance'
195d47e213VS: Allow CMAKE_GENERATOR_INSTANCE to specify portable instanceec8d37b3b1VS: Support version specification in CMAKE_GENERATOR_INSTANCE8e6d930e8cVS: Parse comma-separated fields from CMAKE_GENERATOR_INSTANCE5d1f377737cmVSSetupHelper: Factor out helper to load MSVC toolset version006fe1e919cmVSSetupHelper: Convert wide to narrow strings earlyf5dfc788b8cmVSSetupHelper: Drop unused InstanceId field3213e2595dcmVSSetupHelper: Drop unused ullVersion field152f9978ddHelp: De-duplicate VS instance selection documentation Acked-by: Kitware Robot <kwrobot@kitware.com> Merge-request: !6651
This commit is contained in:
@@ -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
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user