
Since commit f972e4fd3a
(cmVSGenerator: Add support for two-part toolset
versions for Visual Studio, 2022-09-01, v3.25.0-rc1~180^2), if a
two-part toolset version is requested, we fail early if globbing finds
no auxiliary toolsets with that version. This broke our existing
support for detecting when the default toolset matches the two-part
version requested. Fix the logic to ignore the two-part globbing
results if they are empty so we fall through to checking the default
version.
Fixes: #24107
1023 lines
32 KiB
C++
1023 lines
32 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
#include "cmGlobalVisualStudioVersionedGenerator.h"
|
|
|
|
#include <cstring>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <cmext/string_view>
|
|
|
|
#include "cmsys/FStream.hxx"
|
|
#include "cmsys/Glob.hxx"
|
|
#include "cmsys/RegularExpression.hxx"
|
|
|
|
#include "cmDocumentationEntry.h"
|
|
#include "cmGlobalGenerator.h"
|
|
#include "cmGlobalGeneratorFactory.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmMessageType.h"
|
|
#include "cmStateTypes.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cmVSSetupHelper.h"
|
|
#include "cmake.h"
|
|
|
|
#ifndef IMAGE_FILE_MACHINE_ARM64
|
|
# define IMAGE_FILE_MACHINE_ARM64 0xaa64 // ARM64 Little-Endian
|
|
#endif
|
|
|
|
static bool VSIsWow64()
|
|
{
|
|
BOOL isWow64 = false;
|
|
return IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64;
|
|
}
|
|
|
|
static bool VSIsArm64Host()
|
|
{
|
|
typedef BOOL(WINAPI * CM_ISWOW64PROCESS2)(
|
|
HANDLE hProcess, USHORT * pProcessMachine, USHORT * pNativeMachine);
|
|
|
|
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
|
|
# define CM_VS_GCC_DIAGNOSTIC_PUSHED
|
|
# pragma GCC diagnostic push
|
|
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
|
#endif
|
|
static const CM_ISWOW64PROCESS2 s_IsWow64Process2Impl =
|
|
(CM_ISWOW64PROCESS2)GetProcAddress(
|
|
GetModuleHandleW(L"api-ms-win-core-wow64-l1-1-1.dll"),
|
|
"IsWow64Process2");
|
|
#ifdef CM_VS_GCC_DIAGNOSTIC_PUSHED
|
|
# pragma GCC diagnostic pop
|
|
# undef CM_VS_GCC_DIAGNOSTIC_PUSHED
|
|
#endif
|
|
|
|
USHORT processMachine, nativeMachine;
|
|
|
|
return s_IsWow64Process2Impl != nullptr &&
|
|
s_IsWow64Process2Impl(GetCurrentProcess(), &processMachine,
|
|
&nativeMachine) &&
|
|
nativeMachine == IMAGE_FILE_MACHINE_ARM64;
|
|
}
|
|
|
|
static bool VSHasDotNETFrameworkArm64()
|
|
{
|
|
std::string dotNetArm64;
|
|
return cmSystemTools::ReadRegistryValue(
|
|
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\.NETFramework;InstallRootArm64",
|
|
dotNetArm64, cmSystemTools::KeyWOW64_64);
|
|
}
|
|
|
|
static bool VSIsWindows11OrGreater()
|
|
{
|
|
cmSystemTools::WindowsVersion const windowsVersion =
|
|
cmSystemTools::GetWindowsVersion();
|
|
return (windowsVersion.dwMajorVersion > 10 ||
|
|
(windowsVersion.dwMajorVersion == 10 &&
|
|
windowsVersion.dwMinorVersion > 0) ||
|
|
(windowsVersion.dwMajorVersion == 10 &&
|
|
windowsVersion.dwMinorVersion == 0 &&
|
|
windowsVersion.dwBuildNumber >= 22000));
|
|
}
|
|
|
|
static std::string VSHostPlatformName()
|
|
{
|
|
if (VSIsArm64Host()) {
|
|
return "ARM64";
|
|
} else if (VSIsWow64()) {
|
|
return "x64";
|
|
} else {
|
|
#if defined(_M_ARM)
|
|
return "ARM";
|
|
#elif defined(_M_IA64)
|
|
return "Itanium";
|
|
#elif defined(_WIN64)
|
|
return "x64";
|
|
#else
|
|
return "Win32";
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static std::string VSHostArchitecture(
|
|
cmGlobalVisualStudioGenerator::VSVersion v)
|
|
{
|
|
if (VSIsArm64Host()) {
|
|
return v >= cmGlobalVisualStudioGenerator::VSVersion::VS17 ? "ARM64" : "";
|
|
} else if (VSIsWow64()) {
|
|
return "x64";
|
|
} else {
|
|
#if defined(_M_ARM)
|
|
return "";
|
|
#elif defined(_M_IA64)
|
|
return "";
|
|
#elif defined(_WIN64)
|
|
return "x64";
|
|
#else
|
|
return "x86";
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static unsigned int VSVersionToMajor(
|
|
cmGlobalVisualStudioGenerator::VSVersion v)
|
|
{
|
|
switch (v) {
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS9:
|
|
return 9;
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS11:
|
|
return 11;
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS12:
|
|
return 12;
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS14:
|
|
return 14;
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS15:
|
|
return 15;
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS16:
|
|
return 16;
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS17:
|
|
return 17;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const char* VSVersionToToolset(
|
|
cmGlobalVisualStudioGenerator::VSVersion v)
|
|
{
|
|
switch (v) {
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS9:
|
|
return "v90";
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS11:
|
|
return "v110";
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS12:
|
|
return "v120";
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS14:
|
|
return "v140";
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS15:
|
|
return "v141";
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS16:
|
|
return "v142";
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS17:
|
|
return "v143";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
static std::string VSVersionToMajorString(
|
|
cmGlobalVisualStudioGenerator::VSVersion v)
|
|
{
|
|
switch (v) {
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS9:
|
|
return "9";
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS11:
|
|
return "11";
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS12:
|
|
return "12";
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS14:
|
|
return "14";
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS15:
|
|
return "15";
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS16:
|
|
return "16";
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS17:
|
|
return "17";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
static const char* VSVersionToAndroidToolset(
|
|
cmGlobalVisualStudioGenerator::VSVersion v)
|
|
{
|
|
switch (v) {
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS9:
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS11:
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS12:
|
|
return "";
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS14:
|
|
return "Clang_3_8";
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS15:
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS16:
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS17:
|
|
return "Clang_5_0";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
static const char vs15generatorName[] = "Visual Studio 15 2017";
|
|
|
|
// Map generator name without year to name with year.
|
|
static const char* cmVS15GenName(const std::string& name, std::string& genName)
|
|
{
|
|
if (strncmp(name.c_str(), vs15generatorName,
|
|
sizeof(vs15generatorName) - 6) != 0) {
|
|
return 0;
|
|
}
|
|
const char* p = name.c_str() + sizeof(vs15generatorName) - 6;
|
|
if (cmHasLiteralPrefix(p, " 2017")) {
|
|
p += 5;
|
|
}
|
|
genName = std::string(vs15generatorName) + p;
|
|
return p;
|
|
}
|
|
|
|
class cmGlobalVisualStudioVersionedGenerator::Factory15
|
|
: public cmGlobalGeneratorFactory
|
|
{
|
|
public:
|
|
std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
|
|
const std::string& name, bool allowArch, cmake* cm) const override
|
|
{
|
|
std::string genName;
|
|
const char* p = cmVS15GenName(name, genName);
|
|
if (!p) {
|
|
return std::unique_ptr<cmGlobalGenerator>();
|
|
}
|
|
if (!*p) {
|
|
return std::unique_ptr<cmGlobalGenerator>(
|
|
new cmGlobalVisualStudioVersionedGenerator(
|
|
cmGlobalVisualStudioGenerator::VSVersion::VS15, cm, genName, ""));
|
|
}
|
|
if (!allowArch || *p++ != ' ') {
|
|
return std::unique_ptr<cmGlobalGenerator>();
|
|
}
|
|
if (strcmp(p, "Win64") == 0) {
|
|
return std::unique_ptr<cmGlobalGenerator>(
|
|
new cmGlobalVisualStudioVersionedGenerator(
|
|
cmGlobalVisualStudioGenerator::VSVersion::VS15, cm, genName, "x64"));
|
|
}
|
|
if (strcmp(p, "ARM") == 0) {
|
|
return std::unique_ptr<cmGlobalGenerator>(
|
|
new cmGlobalVisualStudioVersionedGenerator(
|
|
cmGlobalVisualStudioGenerator::VSVersion::VS15, cm, genName, "ARM"));
|
|
}
|
|
return std::unique_ptr<cmGlobalGenerator>();
|
|
}
|
|
|
|
void GetDocumentation(cmDocumentationEntry& entry) const override
|
|
{
|
|
entry.Name = std::string(vs15generatorName) + " [arch]";
|
|
entry.Brief = "Generates Visual Studio 2017 project files. "
|
|
"Optional [arch] can be \"Win64\" or \"ARM\".";
|
|
}
|
|
|
|
std::vector<std::string> GetGeneratorNames() const override
|
|
{
|
|
std::vector<std::string> names;
|
|
names.push_back(vs15generatorName);
|
|
return names;
|
|
}
|
|
|
|
std::vector<std::string> GetGeneratorNamesWithPlatform() const override
|
|
{
|
|
std::vector<std::string> names;
|
|
names.push_back(vs15generatorName + std::string(" ARM"));
|
|
names.push_back(vs15generatorName + std::string(" Win64"));
|
|
return names;
|
|
}
|
|
|
|
bool SupportsToolset() const override { return true; }
|
|
bool SupportsPlatform() const override { return true; }
|
|
|
|
std::vector<std::string> GetKnownPlatforms() const override
|
|
{
|
|
std::vector<std::string> platforms;
|
|
platforms.emplace_back("x64");
|
|
platforms.emplace_back("Win32");
|
|
platforms.emplace_back("ARM");
|
|
platforms.emplace_back("ARM64");
|
|
return platforms;
|
|
}
|
|
|
|
std::string GetDefaultPlatformName() const override { return "Win32"; }
|
|
};
|
|
|
|
std::unique_ptr<cmGlobalGeneratorFactory>
|
|
cmGlobalVisualStudioVersionedGenerator::NewFactory15()
|
|
{
|
|
return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory15);
|
|
}
|
|
|
|
static const char vs16generatorName[] = "Visual Studio 16 2019";
|
|
static const char vs17generatorName[] = "Visual Studio 17 2022";
|
|
|
|
// Map generator name without year to name with year.
|
|
static const char* cmVS16GenName(const std::string& name, std::string& genName)
|
|
{
|
|
if (strncmp(name.c_str(), vs16generatorName,
|
|
sizeof(vs16generatorName) - 6) != 0) {
|
|
return 0;
|
|
}
|
|
const char* p = name.c_str() + sizeof(vs16generatorName) - 6;
|
|
if (cmHasLiteralPrefix(p, " 2019")) {
|
|
p += 5;
|
|
}
|
|
genName = std::string(vs16generatorName) + p;
|
|
return p;
|
|
}
|
|
|
|
static const char* cmVS17GenName(const std::string& name, std::string& genName)
|
|
{
|
|
if (strncmp(name.c_str(), vs17generatorName,
|
|
sizeof(vs17generatorName) - 6) != 0) {
|
|
return 0;
|
|
}
|
|
const char* p = name.c_str() + sizeof(vs17generatorName) - 6;
|
|
if (cmHasLiteralPrefix(p, " 2022")) {
|
|
p += 5;
|
|
}
|
|
genName = std::string(vs17generatorName) + p;
|
|
return p;
|
|
}
|
|
|
|
class cmGlobalVisualStudioVersionedGenerator::Factory16
|
|
: public cmGlobalGeneratorFactory
|
|
{
|
|
public:
|
|
std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
|
|
const std::string& name, bool /*allowArch*/, cmake* cm) const override
|
|
{
|
|
std::string genName;
|
|
const char* p = cmVS16GenName(name, genName);
|
|
if (!p) {
|
|
return std::unique_ptr<cmGlobalGenerator>();
|
|
}
|
|
if (!*p) {
|
|
return std::unique_ptr<cmGlobalGenerator>(
|
|
new cmGlobalVisualStudioVersionedGenerator(
|
|
cmGlobalVisualStudioGenerator::VSVersion::VS16, cm, genName, ""));
|
|
}
|
|
return std::unique_ptr<cmGlobalGenerator>();
|
|
}
|
|
|
|
void GetDocumentation(cmDocumentationEntry& entry) const override
|
|
{
|
|
entry.Name = std::string(vs16generatorName);
|
|
entry.Brief = "Generates Visual Studio 2019 project files. "
|
|
"Use -A option to specify architecture.";
|
|
}
|
|
|
|
std::vector<std::string> GetGeneratorNames() const override
|
|
{
|
|
std::vector<std::string> names;
|
|
names.push_back(vs16generatorName);
|
|
return names;
|
|
}
|
|
|
|
std::vector<std::string> GetGeneratorNamesWithPlatform() const override
|
|
{
|
|
return std::vector<std::string>();
|
|
}
|
|
|
|
bool SupportsToolset() const override { return true; }
|
|
bool SupportsPlatform() const override { return true; }
|
|
|
|
std::vector<std::string> GetKnownPlatforms() const override
|
|
{
|
|
std::vector<std::string> platforms;
|
|
platforms.emplace_back("x64");
|
|
platforms.emplace_back("Win32");
|
|
platforms.emplace_back("ARM");
|
|
platforms.emplace_back("ARM64");
|
|
platforms.emplace_back("ARM64EC");
|
|
return platforms;
|
|
}
|
|
|
|
std::string GetDefaultPlatformName() const override
|
|
{
|
|
return VSHostPlatformName();
|
|
}
|
|
};
|
|
|
|
std::unique_ptr<cmGlobalGeneratorFactory>
|
|
cmGlobalVisualStudioVersionedGenerator::NewFactory16()
|
|
{
|
|
return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory16);
|
|
}
|
|
|
|
class cmGlobalVisualStudioVersionedGenerator::Factory17
|
|
: public cmGlobalGeneratorFactory
|
|
{
|
|
public:
|
|
std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
|
|
const std::string& name, bool /*allowArch*/, cmake* cm) const override
|
|
{
|
|
std::string genName;
|
|
const char* p = cmVS17GenName(name, genName);
|
|
if (!p) {
|
|
return std::unique_ptr<cmGlobalGenerator>();
|
|
}
|
|
if (!*p) {
|
|
return std::unique_ptr<cmGlobalGenerator>(
|
|
new cmGlobalVisualStudioVersionedGenerator(
|
|
cmGlobalVisualStudioGenerator::VSVersion::VS17, cm, genName, ""));
|
|
}
|
|
return std::unique_ptr<cmGlobalGenerator>();
|
|
}
|
|
|
|
void GetDocumentation(cmDocumentationEntry& entry) const override
|
|
{
|
|
entry.Name = std::string(vs17generatorName);
|
|
entry.Brief = "Generates Visual Studio 2022 project files. "
|
|
"Use -A option to specify architecture.";
|
|
}
|
|
|
|
std::vector<std::string> GetGeneratorNames() const override
|
|
{
|
|
std::vector<std::string> names;
|
|
names.push_back(vs17generatorName);
|
|
return names;
|
|
}
|
|
|
|
std::vector<std::string> GetGeneratorNamesWithPlatform() const override
|
|
{
|
|
return std::vector<std::string>();
|
|
}
|
|
|
|
bool SupportsToolset() const override { return true; }
|
|
bool SupportsPlatform() const override { return true; }
|
|
|
|
std::vector<std::string> GetKnownPlatforms() const override
|
|
{
|
|
std::vector<std::string> platforms;
|
|
platforms.emplace_back("x64");
|
|
platforms.emplace_back("Win32");
|
|
platforms.emplace_back("ARM");
|
|
platforms.emplace_back("ARM64");
|
|
platforms.emplace_back("ARM64EC");
|
|
return platforms;
|
|
}
|
|
|
|
std::string GetDefaultPlatformName() const override
|
|
{
|
|
return VSHostPlatformName();
|
|
}
|
|
};
|
|
|
|
std::unique_ptr<cmGlobalGeneratorFactory>
|
|
cmGlobalVisualStudioVersionedGenerator::NewFactory17()
|
|
{
|
|
return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory17);
|
|
}
|
|
|
|
cmGlobalVisualStudioVersionedGenerator::cmGlobalVisualStudioVersionedGenerator(
|
|
VSVersion version, cmake* cm, const std::string& name,
|
|
std::string const& platformInGeneratorName)
|
|
: cmGlobalVisualStudio14Generator(cm, name, platformInGeneratorName)
|
|
, vsSetupAPIHelper(VSVersionToMajor(version))
|
|
{
|
|
this->Version = version;
|
|
this->ExpressEdition = false;
|
|
this->DefaultPlatformToolset = VSVersionToToolset(this->Version);
|
|
this->DefaultAndroidToolset = VSVersionToAndroidToolset(this->Version);
|
|
this->DefaultCLFlagTableName = VSVersionToToolset(this->Version);
|
|
this->DefaultCSharpFlagTableName = VSVersionToToolset(this->Version);
|
|
this->DefaultLinkFlagTableName = VSVersionToToolset(this->Version);
|
|
if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS16) {
|
|
this->DefaultPlatformName = VSHostPlatformName();
|
|
this->DefaultPlatformToolsetHostArchitecture =
|
|
VSHostArchitecture(this->Version);
|
|
}
|
|
if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS17) {
|
|
// FIXME: Search for an existing framework? Under '%ProgramFiles(x86)%',
|
|
// see 'Reference Assemblies\Microsoft\Framework\.NETFramework'.
|
|
// Use a version installed by VS 2022 without a separate component.
|
|
this->DefaultTargetFrameworkVersion = "v4.7.2";
|
|
}
|
|
}
|
|
|
|
bool cmGlobalVisualStudioVersionedGenerator::MatchesGeneratorName(
|
|
const std::string& name) const
|
|
{
|
|
std::string genName;
|
|
switch (this->Version) {
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS9:
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS11:
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS12:
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS14:
|
|
break;
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS15:
|
|
if (cmVS15GenName(name, genName)) {
|
|
return genName == this->GetName();
|
|
}
|
|
break;
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS16:
|
|
if (cmVS16GenName(name, genName)) {
|
|
return genName == this->GetName();
|
|
}
|
|
break;
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS17:
|
|
if (cmVS17GenName(name, genName)) {
|
|
return genName == this->GetName();
|
|
}
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool cmGlobalVisualStudioVersionedGenerator::SetGeneratorInstance(
|
|
std::string const& i, cmMakefile* mf)
|
|
{
|
|
if (this->LastGeneratorInstanceString &&
|
|
i == *(this->LastGeneratorInstanceString)) {
|
|
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()) {
|
|
vsInstance = i;
|
|
if (!this->vsSetupAPIHelper.SetVSInstance(
|
|
this->GeneratorInstance, this->GeneratorInstanceVersion)) {
|
|
std::ostringstream e;
|
|
/* clang-format off */
|
|
e <<
|
|
"Generator\n"
|
|
" " << this->GetName() << "\n"
|
|
"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;
|
|
}
|
|
} else if (!this->vsSetupAPIHelper.GetVSInstanceInfo(vsInstance)) {
|
|
std::ostringstream e;
|
|
/* clang-format off */
|
|
e <<
|
|
"Generator\n"
|
|
" " << this->GetName() << "\n"
|
|
"could not find any instance of Visual Studio.\n";
|
|
/* clang-format on */
|
|
mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
|
|
return false;
|
|
}
|
|
|
|
// Save the selected instance persistently.
|
|
std::string genInstance = mf->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE");
|
|
if (vsInstance != genInstance) {
|
|
this->CMakeInstance->AddCacheEntry("CMAKE_GENERATOR_INSTANCE", vsInstance,
|
|
"Generator instance identifier.",
|
|
cmStateEnums::INTERNAL);
|
|
}
|
|
|
|
// The selected instance may have a different MSBuild than previously found.
|
|
this->MSBuildCommandInitialized = false;
|
|
|
|
this->LastGeneratorInstanceString = i;
|
|
|
|
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
|
|
{
|
|
return vsSetupAPIHelper.GetVSInstanceInfo(dir);
|
|
}
|
|
|
|
cm::optional<std::string>
|
|
cmGlobalVisualStudioVersionedGenerator::GetVSInstanceVersion() const
|
|
{
|
|
cm::optional<std::string> result;
|
|
std::string vsInstanceVersion;
|
|
if (vsSetupAPIHelper.GetVSInstanceVersion(vsInstanceVersion)) {
|
|
result = vsInstanceVersion;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool cmGlobalVisualStudioVersionedGenerator::IsStdOutEncodingSupported() const
|
|
{
|
|
// Supported from Visual Studio 16.7 Preview 3.
|
|
if (this->Version > cmGlobalVisualStudioGenerator::VSVersion::VS16) {
|
|
return true;
|
|
}
|
|
if (this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS16) {
|
|
return false;
|
|
}
|
|
static std::string const vsVer16_7_P2 = "16.7.30128.36";
|
|
cm::optional<std::string> vsVer = this->GetVSInstanceVersion();
|
|
return (vsVer &&
|
|
cmSystemTools::VersionCompareGreaterEq(*vsVer, vsVer16_7_P2));
|
|
}
|
|
|
|
bool cmGlobalVisualStudioVersionedGenerator::IsUtf8EncodingSupported() const
|
|
{
|
|
// Supported from Visual Studio 16.10 Preview 2.
|
|
if (this->Version > cmGlobalVisualStudioGenerator::VSVersion::VS16) {
|
|
return true;
|
|
}
|
|
if (this->Version < cmGlobalVisualStudioGenerator::VSVersion::VS16) {
|
|
return false;
|
|
}
|
|
static std::string const vsVer16_10_P2 = "16.10.31213.239";
|
|
cm::optional<std::string> vsVer = this->GetVSInstanceVersion();
|
|
return (vsVer &&
|
|
cmSystemTools::VersionCompareGreaterEq(*vsVer, vsVer16_10_P2));
|
|
}
|
|
|
|
const char*
|
|
cmGlobalVisualStudioVersionedGenerator::GetAndroidApplicationTypeRevision()
|
|
const
|
|
{
|
|
switch (this->Version) {
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS9:
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS11:
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS12:
|
|
return "";
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS14:
|
|
return "2.0";
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS15:
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS16:
|
|
case cmGlobalVisualStudioGenerator::VSVersion::VS17:
|
|
return "3.0";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
cmGlobalVisualStudioVersionedGenerator::AuxToolset
|
|
cmGlobalVisualStudioVersionedGenerator::FindAuxToolset(
|
|
std::string& version, std::string& props) const
|
|
{
|
|
if (version.empty()) {
|
|
return AuxToolset::None;
|
|
}
|
|
|
|
std::string instancePath;
|
|
this->GetVSInstance(instancePath);
|
|
cmSystemTools::ConvertToUnixSlashes(instancePath);
|
|
|
|
// Translate three-component format accepted by "vcvarsall -vcvars_ver=".
|
|
cmsys::RegularExpression threeComponentRegex(
|
|
"^([0-9]+\\.[0-9]+)\\.[0-9][0-9][0-9][0-9][0-9]$");
|
|
// The two-component format represents the two major components of the
|
|
// three-component format
|
|
cmsys::RegularExpression twoComponentRegex("^([0-9]+\\.[0-9]+)$");
|
|
if (threeComponentRegex.find(version)) {
|
|
// Load "VC/Auxiliary/Build/*/Microsoft.VCToolsVersion.*.txt" files
|
|
// with two matching components to check their three-component version.
|
|
std::string const& twoComponent = threeComponentRegex.match(1);
|
|
std::string pattern =
|
|
cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s, twoComponent,
|
|
"*/Microsoft.VCToolsVersion."_s, twoComponent, "*.txt"_s);
|
|
cmsys::Glob glob;
|
|
glob.SetRecurseThroughSymlinks(false);
|
|
if (glob.FindFiles(pattern)) {
|
|
for (std::string const& txt : glob.GetFiles()) {
|
|
std::string ver;
|
|
cmsys::ifstream fin(txt.c_str());
|
|
if (fin && std::getline(fin, ver)) {
|
|
// Strip trailing whitespace.
|
|
ver = ver.substr(0, ver.find_first_not_of("0123456789."));
|
|
// If the three-component version matches, translate it to
|
|
// that used by the "Microsoft.VCToolsVersion.*.txt" file name.
|
|
if (ver == version) {
|
|
cmsys::RegularExpression extractVersion(
|
|
"VCToolsVersion\\.([0-9.]+)\\.txt$");
|
|
if (extractVersion.find(txt)) {
|
|
version = extractVersion.match(1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (twoComponentRegex.find(version)) {
|
|
std::string const& twoComponent = twoComponentRegex.match(1);
|
|
std::string pattern =
|
|
cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s, twoComponent,
|
|
"*/Microsoft.VCToolsVersion."_s, twoComponent, "*.txt"_s);
|
|
cmsys::Glob glob;
|
|
glob.SetRecurseThroughSymlinks(false);
|
|
if (glob.FindFiles(pattern) && !glob.GetFiles().empty()) {
|
|
// Since we are only using the first two components of the
|
|
// toolset version, we require a single match.
|
|
if (glob.GetFiles().size() == 1) {
|
|
std::string const& txt = glob.GetFiles()[0];
|
|
std::string ver;
|
|
cmsys::ifstream fin(txt.c_str());
|
|
if (fin && std::getline(fin, ver)) {
|
|
// Strip trailing whitespace.
|
|
ver = ver.substr(0, ver.find_first_not_of("0123456789."));
|
|
// We assume the version is correct, since it is the only one that
|
|
// matched.
|
|
cmsys::RegularExpression extractVersion(
|
|
"VCToolsVersion\\.([0-9.]+)\\.txt$");
|
|
if (extractVersion.find(txt)) {
|
|
version = extractVersion.match(1);
|
|
}
|
|
}
|
|
} else {
|
|
props = cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s);
|
|
return AuxToolset::PropsIndeterminate;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cmSystemTools::VersionCompareGreaterEq(version, "14.20")) {
|
|
props = cmStrCat(instancePath, "/VC/Auxiliary/Build."_s, version,
|
|
"/Microsoft.VCToolsVersion."_s, version, ".props"_s);
|
|
if (cmSystemTools::PathExists(props)) {
|
|
return AuxToolset::PropsExist;
|
|
}
|
|
}
|
|
props = cmStrCat(instancePath, "/VC/Auxiliary/Build/"_s, version,
|
|
"/Microsoft.VCToolsVersion."_s, version, ".props"_s);
|
|
if (cmSystemTools::PathExists(props)) {
|
|
return AuxToolset::PropsExist;
|
|
}
|
|
|
|
// Accept the toolset version that is default in the current VS version
|
|
// by matching the name later VS versions will use for the SxS props files.
|
|
std::string vcToolsetVersion;
|
|
if (this->vsSetupAPIHelper.GetVCToolsetVersion(vcToolsetVersion)) {
|
|
// Accept an exact-match (three-component version).
|
|
if (version == vcToolsetVersion) {
|
|
return AuxToolset::Default;
|
|
}
|
|
|
|
// Accept known SxS props file names using four version components
|
|
// in VS versions later than the current.
|
|
if (version == "14.28.16.9" && vcToolsetVersion == "14.28.29910") {
|
|
return AuxToolset::Default;
|
|
}
|
|
if (version == "14.29.16.10" && vcToolsetVersion == "14.29.30037") {
|
|
return AuxToolset::Default;
|
|
}
|
|
if (version == "14.29.16.11" && vcToolsetVersion == "14.29.30133") {
|
|
return AuxToolset::Default;
|
|
}
|
|
|
|
// The first two components of the default toolset version typically
|
|
// match the name used by later VS versions for the SxS props files.
|
|
cmsys::RegularExpression twoComponent("^([0-9]+\\.[0-9]+)");
|
|
if (twoComponent.find(version)) {
|
|
std::string const versionPrefix = cmStrCat(twoComponent.match(1), '.');
|
|
if (cmHasPrefix(vcToolsetVersion, versionPrefix)) {
|
|
return AuxToolset::Default;
|
|
}
|
|
}
|
|
}
|
|
|
|
return AuxToolset::PropsMissing;
|
|
}
|
|
|
|
bool cmGlobalVisualStudioVersionedGenerator::InitializeWindows(cmMakefile* mf)
|
|
{
|
|
// If the Win 8.1 SDK is installed then we can select a SDK matching
|
|
// the target Windows version.
|
|
if (this->IsWin81SDKInstalled()) {
|
|
// VS 2019 does not default to 8.1 so specify it explicitly when needed.
|
|
if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS16 &&
|
|
!cmSystemTools::VersionCompareGreater(this->SystemVersion, "8.1")) {
|
|
this->SetWindowsTargetPlatformVersion("8.1", mf);
|
|
return true;
|
|
}
|
|
return cmGlobalVisualStudio14Generator::InitializeWindows(mf);
|
|
}
|
|
// Otherwise we must choose a Win 10 SDK even if we are not targeting
|
|
// Windows 10.
|
|
return this->SelectWindows10SDK(mf, false);
|
|
}
|
|
|
|
bool cmGlobalVisualStudioVersionedGenerator::SelectWindowsStoreToolset(
|
|
std::string& toolset) const
|
|
{
|
|
if (cmHasLiteralPrefix(this->SystemVersion, "10.0")) {
|
|
if (this->IsWindowsStoreToolsetInstalled() &&
|
|
this->IsWindowsDesktopToolsetInstalled()) {
|
|
toolset = VSVersionToToolset(this->Version);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
return this->cmGlobalVisualStudio14Generator::SelectWindowsStoreToolset(
|
|
toolset);
|
|
}
|
|
|
|
bool cmGlobalVisualStudioVersionedGenerator::IsWindowsDesktopToolsetInstalled()
|
|
const
|
|
{
|
|
return vsSetupAPIHelper.IsVSInstalled();
|
|
}
|
|
|
|
bool cmGlobalVisualStudioVersionedGenerator::IsWindowsStoreToolsetInstalled()
|
|
const
|
|
{
|
|
return vsSetupAPIHelper.IsWin10SDKInstalled();
|
|
}
|
|
|
|
bool cmGlobalVisualStudioVersionedGenerator::IsWin81SDKInstalled() const
|
|
{
|
|
// Does the VS installer tool know about one?
|
|
if (vsSetupAPIHelper.IsWin81SDKInstalled()) {
|
|
return true;
|
|
}
|
|
|
|
// Does the registry know about one (e.g. from VS 2015)?
|
|
std::string win81Root;
|
|
if (cmSystemTools::ReadRegistryValue(
|
|
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\"
|
|
"Windows Kits\\Installed Roots;KitsRoot81",
|
|
win81Root, cmSystemTools::KeyWOW64_32) ||
|
|
cmSystemTools::ReadRegistryValue(
|
|
"HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\"
|
|
"Windows Kits\\Installed Roots;KitsRoot81",
|
|
win81Root, cmSystemTools::KeyWOW64_32)) {
|
|
return cmSystemTools::FileExists(win81Root + "/include/um/windows.h",
|
|
true);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::string
|
|
cmGlobalVisualStudioVersionedGenerator::GetWindows10SDKMaxVersionDefault(
|
|
cmMakefile*) const
|
|
{
|
|
return std::string();
|
|
}
|
|
|
|
cm::optional<std::string>
|
|
cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommandEarly(cmMakefile* mf)
|
|
{
|
|
std::string instance = mf->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE");
|
|
if (!this->SetGeneratorInstance(instance, mf)) {
|
|
cmSystemTools::SetFatalErrorOccurred();
|
|
return {};
|
|
}
|
|
return this->cmGlobalVisualStudio14Generator::FindMSBuildCommandEarly(mf);
|
|
}
|
|
|
|
std::string cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommand()
|
|
{
|
|
std::string msbuild;
|
|
|
|
// Ask Visual Studio Installer tool.
|
|
std::string vs;
|
|
if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
|
|
if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS17) {
|
|
if (VSIsArm64Host()) {
|
|
if (VSHasDotNETFrameworkArm64()) {
|
|
msbuild = vs + "/MSBuild/Current/Bin/arm64/MSBuild.exe";
|
|
if (cmSystemTools::FileExists(msbuild)) {
|
|
return msbuild;
|
|
}
|
|
}
|
|
if (VSIsWindows11OrGreater()) {
|
|
msbuild = vs + "/MSBuild/Current/Bin/amd64/MSBuild.exe";
|
|
if (cmSystemTools::FileExists(msbuild)) {
|
|
return msbuild;
|
|
}
|
|
}
|
|
} else {
|
|
msbuild = vs + "/MSBuild/Current/Bin/amd64/MSBuild.exe";
|
|
if (cmSystemTools::FileExists(msbuild)) {
|
|
return msbuild;
|
|
}
|
|
}
|
|
}
|
|
msbuild = vs + "/MSBuild/Current/Bin/MSBuild.exe";
|
|
if (cmSystemTools::FileExists(msbuild)) {
|
|
return msbuild;
|
|
}
|
|
msbuild = vs + "/MSBuild/15.0/Bin/MSBuild.exe";
|
|
if (cmSystemTools::FileExists(msbuild)) {
|
|
return msbuild;
|
|
}
|
|
}
|
|
|
|
msbuild = "MSBuild.exe";
|
|
return msbuild;
|
|
}
|
|
|
|
std::string cmGlobalVisualStudioVersionedGenerator::FindDevEnvCommand()
|
|
{
|
|
std::string devenv;
|
|
|
|
// Ask Visual Studio Installer tool.
|
|
std::string vs;
|
|
if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
|
|
devenv = vs + "/Common7/IDE/devenv.com";
|
|
if (cmSystemTools::FileExists(devenv)) {
|
|
return devenv;
|
|
}
|
|
}
|
|
|
|
devenv = "devenv.com";
|
|
return devenv;
|
|
}
|