/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmGlobalVisualStudio14Generator.h" #include #include #include #include #include "cmGlobalGenerator.h" #include "cmGlobalGeneratorFactory.h" #include "cmGlobalVisualStudioGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmPolicies.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmValue.h" static const char vs14generatorName[] = "Visual Studio 14 2015"; // Map generator name without year to name with year. static const char* cmVS14GenName(const std::string& name, std::string& genName) { if (strncmp(name.c_str(), vs14generatorName, sizeof(vs14generatorName) - 6) != 0) { return nullptr; } const char* p = name.c_str() + sizeof(vs14generatorName) - 6; if (cmHasLiteralPrefix(p, " 2015")) { p += 5; } genName = std::string(vs14generatorName) + p; return p; } class cmGlobalVisualStudio14Generator::Factory : public cmGlobalGeneratorFactory { public: std::unique_ptr CreateGlobalGenerator( const std::string& name, bool allowArch, cmake* cm) const override { std::string genName; const char* p = cmVS14GenName(name, genName); if (!p) { return std::unique_ptr(); } if (!*p) { return std::unique_ptr( new cmGlobalVisualStudio14Generator(cm, genName, "")); } if (!allowArch || *p++ != ' ') { return std::unique_ptr(); } if (strcmp(p, "Win64") == 0) { return std::unique_ptr( new cmGlobalVisualStudio14Generator(cm, genName, "x64")); } if (strcmp(p, "ARM") == 0) { return std::unique_ptr( new cmGlobalVisualStudio14Generator(cm, genName, "ARM")); } return std::unique_ptr(); } cmDocumentationEntry GetDocumentation() const override { return { cmStrCat(vs14generatorName, " [arch]"), "Generates Visual Studio 2015 project files. " "Optional [arch] can be \"Win64\" or \"ARM\"." }; } std::vector GetGeneratorNames() const override { std::vector names; names.push_back(vs14generatorName); return names; } std::vector GetGeneratorNamesWithPlatform() const override { std::vector names; names.emplace_back(cmStrCat(vs14generatorName, " ARM")); names.emplace_back(cmStrCat(vs14generatorName, " Win64")); return names; } bool SupportsToolset() const override { return true; } bool SupportsPlatform() const override { return true; } std::vector GetKnownPlatforms() const override { std::vector platforms; platforms.emplace_back("x64"); platforms.emplace_back("Win32"); platforms.emplace_back("ARM"); return platforms; } std::string GetDefaultPlatformName() const override { return "Win32"; } }; std::unique_ptr cmGlobalVisualStudio14Generator::NewFactory() { return std::unique_ptr(new Factory); } cmGlobalVisualStudio14Generator::cmGlobalVisualStudio14Generator( cmake* cm, const std::string& name, std::string const& platformInGeneratorName) : cmGlobalVisualStudio12Generator(cm, name, platformInGeneratorName) { std::string vc14Express; this->ExpressEdition = cmSystemTools::ReadRegistryValue( "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress\\14.0\\Setup\\VC;" "ProductDir", vc14Express, cmSystemTools::KeyWOW64_32); this->DefaultPlatformToolset = "v140"; this->DefaultAndroidToolset = "Clang_3_8"; this->DefaultCLFlagTableName = "v140"; this->DefaultCSharpFlagTableName = "v140"; this->DefaultLibFlagTableName = "v14"; this->DefaultLinkFlagTableName = "v140"; this->DefaultMasmFlagTableName = "v14"; this->DefaultRCFlagTableName = "v14"; this->Version = VSVersion::VS14; } bool cmGlobalVisualStudio14Generator::MatchesGeneratorName( const std::string& name) const { std::string genName; if (cmVS14GenName(name, genName)) { return genName == this->GetName(); } return false; } bool cmGlobalVisualStudio14Generator::InitializePlatformWindows(cmMakefile* mf) { // If a Windows SDK version is explicitly requested, search for it. if (this->GeneratorPlatformVersion) { std::string const& version = *this->GeneratorPlatformVersion; // VS 2019 and above support specifying plain "10.0". if (version == "10.0"_s) { if (this->Version >= VSVersion::VS16) { this->SetWindowsTargetPlatformVersion("10.0", mf); return true; } /* clang-format off */ mf->IssueMessage(MessageType::FATAL_ERROR, cmStrCat( "Generator\n" " ", this->GetName(), "\n" "given platform specification containing a\n" " version=10.0\n" "field. The value 10.0 is only supported by VS 2019 and above.\n" )); /* clang-format on */ return false; } if (cmHasLiteralPrefix(version, "10.0.")) { return this->SelectWindows10SDK(mf); } if (version == "8.1"_s) { if (this->IsWin81SDKInstalled()) { this->SetWindowsTargetPlatformVersion("8.1", mf); return true; } /* clang-format off */ mf->IssueMessage(MessageType::FATAL_ERROR, cmStrCat( "Generator\n" " ", this->GetName(), "\n" "given platform specification containing a\n" " version=8.1\n" "field, but the Windows 8.1 SDK is not installed.\n" )); /* clang-format on */ return false; } if (version.empty()) { /* clang-format off */ mf->IssueMessage(MessageType::FATAL_ERROR, cmStrCat( "Generator\n" " ", this->GetName(), "\n" "given platform specification with empty\n" " version=\n" "field.\n" )); /* clang-format on */ return false; } /* clang-format off */ mf->IssueMessage(MessageType::FATAL_ERROR, cmStrCat( "Generator\n" " ", this->GetName(), "\n" "given platform specification containing a\n" " version=", version, "\n" "field with unsupported value.\n" )); /* clang-format on */ return false; } // If we are targeting Windows 10+, we select a Windows 10 SDK. // If no Windows 8.1 SDK is installed, which is possible with VS 2017 and // higher, then we must choose a Windows 10 SDK anyway. if (cmHasLiteralPrefix(this->SystemVersion, "10.0") || !this->IsWin81SDKInstalled()) { return this->SelectWindows10SDK(mf); } // Under CMP0149 NEW behavior, we search for a Windows 10 SDK even // when targeting older Windows versions, but it is not required. if (mf->GetPolicyStatus(cmPolicies::CMP0149) == cmPolicies::NEW) { std::string const version = this->GetWindows10SDKVersion(mf); if (!version.empty()) { this->SetWindowsTargetPlatformVersion(version, mf); return true; } } // We are not targeting Windows 10+, so fall back to the Windows 8.1 SDK. // For VS 2019 and above we must explicitly specify it. if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS16 && !cmSystemTools::VersionCompareGreater(this->SystemVersion, "8.1")) { this->SetWindowsTargetPlatformVersion("8.1", mf); } return true; } bool cmGlobalVisualStudio14Generator::VerifyNoGeneratorPlatformVersion( cmMakefile* mf) const { if (!this->GeneratorPlatformVersion) { return true; } std::ostringstream e; /* clang-format off */ e << "Generator\n" " " << this->GetName() << "\n" "given platform specification containing a\n" " version=" << *this->GeneratorPlatformVersion << "\n" "field. The version field is not supported when targeting\n" " " << this->SystemName << ' ' << this->SystemVersion << '\n' ; /* clang-format on */ mf->IssueMessage(MessageType::FATAL_ERROR, e.str()); return false; } bool cmGlobalVisualStudio14Generator::InitializeWindowsStore(cmMakefile* mf) { if (!this->SelectWindowsStoreToolset(this->DefaultPlatformToolset)) { std::string e; if (this->DefaultPlatformToolset.empty()) { e = cmStrCat(this->GetName(), " supports Windows Store '8.0', '8.1' and " "'10.0', but not '", this->SystemVersion, "'. Check CMAKE_SYSTEM_VERSION."); } else { e = cmStrCat( "A Windows Store component with CMake requires both the Windows " "Desktop SDK as well as the Windows Store '", this->SystemVersion, "' SDK. Please make sure that you have both installed"); } mf->IssueMessage(MessageType::FATAL_ERROR, e); return false; } return true; } bool cmGlobalVisualStudio14Generator::InitializeAndroid(cmMakefile*) { return true; } bool cmGlobalVisualStudio14Generator::ProcessGeneratorPlatformField( std::string const& key, std::string const& value) { if (key == "version"_s) { this->GeneratorPlatformVersion = value; return true; } return false; } bool cmGlobalVisualStudio14Generator::SelectWindows10SDK(cmMakefile* mf) { // Find the default version of the Windows 10 SDK. std::string const version = this->GetWindows10SDKVersion(mf); if (version.empty()) { if (this->GeneratorPlatformVersion) { mf->IssueMessage( MessageType::FATAL_ERROR, cmStrCat("Generator\n ", this->GetName(), "\ngiven platform specification with\n version=", *this->GeneratorPlatformVersion, "\nfield, but no Windows SDK with that version was found.")); return false; } if (this->SystemName == "WindowsStore"_s) { mf->IssueMessage( MessageType::FATAL_ERROR, "Could not find an appropriate version of the Windows 10 SDK" " installed on this machine"); return false; } } this->SetWindowsTargetPlatformVersion(version, mf); return true; } void cmGlobalVisualStudio14Generator::SetWindowsTargetPlatformVersion( std::string const& version, cmMakefile* mf) { this->WindowsTargetPlatformVersion = version; if (!this->WindowsTargetPlatformVersion.empty() && !cmSystemTools::VersionCompareEqual(this->WindowsTargetPlatformVersion, this->SystemVersion)) { mf->DisplayStatus(cmStrCat("Selecting Windows SDK version ", this->WindowsTargetPlatformVersion, " to target Windows ", this->SystemVersion, '.'), -1); } mf->AddDefinition("CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION", this->WindowsTargetPlatformVersion); } bool cmGlobalVisualStudio14Generator::SelectWindowsStoreToolset( std::string& toolset) const { if (cmHasLiteralPrefix(this->SystemVersion, "10.0")) { if (this->IsWindowsStoreToolsetInstalled() && this->IsWindowsDesktopToolsetInstalled()) { toolset = "v140"; return true; } return false; } return this->cmGlobalVisualStudio12Generator::SelectWindowsStoreToolset( toolset); } bool cmGlobalVisualStudio14Generator::IsWindowsDesktopToolsetInstalled() const { const char desktop10Key[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\" "VisualStudio\\14.0\\VC\\Runtimes"; std::vector vc14; return cmSystemTools::GetRegistrySubKeys(desktop10Key, vc14, cmSystemTools::KeyWOW64_32); } bool cmGlobalVisualStudio14Generator::IsWindowsStoreToolsetInstalled() const { const char universal10Key[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\" "VisualStudio\\14.0\\Setup\\Build Tools for Windows 10;SrcPath"; std::string win10SDK; return cmSystemTools::ReadRegistryValue(universal10Key, win10SDK, cmSystemTools::KeyWOW64_32); } bool cmGlobalVisualStudio14Generator::IsWin81SDKInstalled() const { return true; } std::string cmGlobalVisualStudio14Generator::GetWindows10SDKMaxVersion( cmMakefile* mf) const { // if the given value is set, it can either be OFF/FALSE or a valid SDK // string if (cmValue value = mf->GetDefinition( "CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM")) { // If the value is some off/false value, then there is NO maximum set. if (cmIsOff(value)) { return std::string(); } // If the value is something else, trust that it is a valid SDK value. if (value) { return *value; } // If value is an invalid pointer, leave result unchanged. } return this->GetWindows10SDKMaxVersionDefault(mf); } std::string cmGlobalVisualStudio14Generator::GetWindows10SDKMaxVersionDefault( cmMakefile*) const { // The last Windows 10 SDK version that VS 2015 can target is 10.0.14393.0. // // "VS 2015 Users: The Windows 10 SDK (15063, 16299, 17134, 17763) is // officially only supported for VS 2017." From: // https://blogs.msdn.microsoft.com/chuckw/2018/10/02/windows-10-october-2018-update/ return "10.0.14393.0"; } #if defined(_WIN32) && !defined(__CYGWIN__) struct NoWindowsH { bool operator()(std::string const& p) { return !cmSystemTools::FileExists(cmStrCat(p, "/um/windows.h"), true); } }; class WindowsSDKTooRecent { std::string const& MaxVersion; public: WindowsSDKTooRecent(std::string const& maxVersion) : MaxVersion(maxVersion) { } bool operator()(std::string const& v) { return cmSystemTools::VersionCompareGreater(v, MaxVersion); } }; #endif std::string cmGlobalVisualStudio14Generator::GetWindows10SDKVersion( cmMakefile* mf) { #if defined(_WIN32) && !defined(__CYGWIN__) std::vector win10Roots; { std::string win10Root; if (cmSystemTools::GetEnv("CMAKE_WINDOWS_KITS_10_DIR", win10Root)) { cmSystemTools::ConvertToUnixSlashes(win10Root); win10Roots.push_back(win10Root); } } { // This logic is taken from the vcvarsqueryregistry.bat file from VS2015 // Try HKLM and then HKCU. std::string win10Root; if (cmSystemTools::ReadRegistryValue( "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\" "Windows Kits\\Installed Roots;KitsRoot10", win10Root, cmSystemTools::KeyWOW64_32) || cmSystemTools::ReadRegistryValue( "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\" "Windows Kits\\Installed Roots;KitsRoot10", win10Root, cmSystemTools::KeyWOW64_32)) { cmSystemTools::ConvertToUnixSlashes(win10Root); win10Roots.push_back(win10Root); } } if (win10Roots.empty()) { return std::string(); } std::vector sdks; // Grab the paths of the different SDKs that are installed for (std::string const& i : win10Roots) { std::string path = cmStrCat(i, "/Include/*"); cmSystemTools::GlobDirs(path, sdks); } // Skip SDKs that do not contain because that indicates that // only the UCRT MSIs were installed for them. cm::erase_if(sdks, NoWindowsH()); // Only use the filename, which will be the SDK version. for (std::string& i : sdks) { i = cmSystemTools::GetFilenameName(i); } // Skip SDKs that cannot be used with our toolset, unless the user does not // want to limit the highest supported SDK according to the Microsoft // documentation. std::string maxVersion = this->GetWindows10SDKMaxVersion(mf); if (!maxVersion.empty()) { cm::erase_if(sdks, WindowsSDKTooRecent(maxVersion)); } // Sort the results to make sure we select the most recent one. std::sort(sdks.begin(), sdks.end(), cmSystemTools::VersionCompareGreater); // Look for a SDK exactly matching the requested version, if any. if (this->GeneratorPlatformVersion) { for (std::string const& i : sdks) { if (cmSystemTools::VersionCompareEqual( i, *this->GeneratorPlatformVersion)) { return i; } } // An exact version was requested but not found. // Our caller will issue the error message. return std::string(); } if (mf->GetPolicyStatus(cmPolicies::CMP0149) == cmPolicies::NEW) { if (cm::optional const envVer = cmSystemTools::GetEnvVar("WindowsSDKVersion")) { // Look for a SDK exactly matching the environment variable. for (std::string const& i : sdks) { if (cmSystemTools::VersionCompareEqual(i, *envVer)) { return i; } } } } else { // Look for a SDK exactly matching the target Windows version. for (std::string const& i : sdks) { if (cmSystemTools::VersionCompareEqual(i, this->SystemVersion)) { return i; } } } if (!sdks.empty()) { // Use the latest Windows 10 SDK since the exact version is not available. return sdks.at(0); } #endif (void)mf; // Return an empty string return std::string(); }