aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Karatarakis <alkarata@microsoft.com>2018-06-19 17:51:20 -0700
committerAlexander Karatarakis <alkarata@microsoft.com>2018-06-19 23:07:31 -0700
commitc256ccf4526decec0422d8fc9d91ceb8fcf6160b (patch)
treee1b90d4fbaebfb8ae794659ee4cd223046e8056e
parentdbae3bfe566c4c2d949ca6b2f84c6867f15e46ac (diff)
downloadvcpkg-c256ccf4526decec0422d8fc9d91ceb8fcf6160b.tar.gz
vcpkg-c256ccf4526decec0422d8fc9d91ceb8fcf6160b.zip
Introduce stringrange.h/cpp and visualstudio.h/cpp
-rw-r--r--toolsrc/include/vcpkg/base/stringrange.h33
-rw-r--r--toolsrc/include/vcpkg/commands.h1
-rw-r--r--toolsrc/include/vcpkg/visualstudio.h12
-rw-r--r--toolsrc/src/vcpkg/base/stringrange.cpp79
-rw-r--r--toolsrc/src/vcpkg/commands.fetch.cpp388
-rw-r--r--toolsrc/src/vcpkg/vcpkgpaths.cpp5
-rw-r--r--toolsrc/src/vcpkg/visualstudio.cpp304
-rw-r--r--toolsrc/vcpkglib/vcpkglib.vcxproj4
-rw-r--r--toolsrc/vcpkglib/vcpkglib.vcxproj.filters12
9 files changed, 457 insertions, 381 deletions
diff --git a/toolsrc/include/vcpkg/base/stringrange.h b/toolsrc/include/vcpkg/base/stringrange.h
new file mode 100644
index 000000000..94bd584af
--- /dev/null
+++ b/toolsrc/include/vcpkg/base/stringrange.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <vcpkg/base/optional.h>
+
+#include <string>
+#include <vector>
+
+namespace vcpkg
+{
+ struct VcpkgStringRange
+ {
+ static std::vector<VcpkgStringRange> find_all_enclosed(const VcpkgStringRange& input,
+ const std::string& left_delim,
+ const std::string& right_delim);
+
+ static VcpkgStringRange find_exactly_one_enclosed(const VcpkgStringRange& input,
+ const std::string& left_tag,
+ const std::string& right_tag);
+
+ static Optional<VcpkgStringRange> find_at_most_one_enclosed(const VcpkgStringRange& input,
+ const std::string& left_tag,
+ const std::string& right_tag);
+
+ VcpkgStringRange() = default;
+ VcpkgStringRange(const std::string& s); // Implicit by design
+ VcpkgStringRange(const std::string::const_iterator begin, const std::string::const_iterator end);
+
+ std::string::const_iterator begin;
+ std::string::const_iterator end;
+
+ std::string to_string() const;
+ };
+}
diff --git a/toolsrc/include/vcpkg/commands.h b/toolsrc/include/vcpkg/commands.h
index 21e77aa52..e4ea44334 100644
--- a/toolsrc/include/vcpkg/commands.h
+++ b/toolsrc/include/vcpkg/commands.h
@@ -139,7 +139,6 @@ namespace vcpkg::Commands
namespace Fetch
{
- std::vector<Toolset> find_toolset_instances_preferred_first(const VcpkgPaths& paths);
fs::path get_tool_path(const VcpkgPaths& paths, const std::string& tool);
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths);
}
diff --git a/toolsrc/include/vcpkg/visualstudio.h b/toolsrc/include/vcpkg/visualstudio.h
new file mode 100644
index 000000000..b93b145d9
--- /dev/null
+++ b/toolsrc/include/vcpkg/visualstudio.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#if defined(_WIN32)
+
+#include <vcpkg/vcpkgpaths.h>
+
+namespace vcpkg::VisualStudio
+{
+ std::vector<Toolset> find_toolset_instances_preferred_first(const VcpkgPaths& paths);
+}
+
+#endif
diff --git a/toolsrc/src/vcpkg/base/stringrange.cpp b/toolsrc/src/vcpkg/base/stringrange.cpp
new file mode 100644
index 000000000..47ed3bf8d
--- /dev/null
+++ b/toolsrc/src/vcpkg/base/stringrange.cpp
@@ -0,0 +1,79 @@
+#include "pch.h"
+
+#include <vcpkg/base/checks.h>
+#include <vcpkg/base/stringrange.h>
+
+namespace vcpkg
+{
+ std::vector<VcpkgStringRange> VcpkgStringRange::find_all_enclosed(const VcpkgStringRange& input,
+ const std::string& left_delim,
+ const std::string& right_delim)
+ {
+ std::string::const_iterator it_left = input.begin;
+ std::string::const_iterator it_right = input.begin;
+
+ std::vector<VcpkgStringRange> output;
+
+ while (true)
+ {
+ it_left = std::search(it_right, input.end, left_delim.cbegin(), left_delim.cend());
+ if (it_left == input.end) break;
+
+ it_left += left_delim.length();
+
+ it_right = std::search(it_left, input.end, right_delim.cbegin(), right_delim.cend());
+ if (it_right == input.end) break;
+
+ output.emplace_back(it_left, it_right);
+
+ ++it_right;
+ }
+
+ return output;
+ }
+
+ VcpkgStringRange VcpkgStringRange::find_exactly_one_enclosed(const VcpkgStringRange& input,
+ const std::string& left_tag,
+ const std::string& right_tag)
+ {
+ std::vector<VcpkgStringRange> result = find_all_enclosed(input, left_tag, right_tag);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ result.size() == 1,
+ "Found %d sets of %s.*%s but expected exactly 1, in block:\n%s",
+ result.size(),
+ left_tag,
+ right_tag,
+ input);
+ return result.front();
+ }
+
+ Optional<VcpkgStringRange> VcpkgStringRange::find_at_most_one_enclosed(const VcpkgStringRange& input,
+ const std::string& left_tag,
+ const std::string& right_tag)
+ {
+ std::vector<VcpkgStringRange> result = find_all_enclosed(input, left_tag, right_tag);
+ Checks::check_exit(VCPKG_LINE_INFO,
+ result.size() <= 1,
+ "Found %d sets of %s.*%s but expected at most 1, in block:\n%s",
+ result.size(),
+ left_tag,
+ right_tag,
+ input);
+
+ if (result.empty())
+ {
+ return nullopt;
+ }
+
+ return result.front();
+ }
+
+ VcpkgStringRange::VcpkgStringRange(const std::string& s) : begin(s.cbegin()), end(s.cend()) {}
+
+ VcpkgStringRange::VcpkgStringRange(const std::string::const_iterator begin, const std::string::const_iterator end)
+ : begin(begin), end(end)
+ {
+ }
+
+ std::string VcpkgStringRange::to_string() const { return std::string(this->begin, this->end); }
+}
diff --git a/toolsrc/src/vcpkg/commands.fetch.cpp b/toolsrc/src/vcpkg/commands.fetch.cpp
index be3aabfb6..66af4ec48 100644
--- a/toolsrc/src/vcpkg/commands.fetch.cpp
+++ b/toolsrc/src/vcpkg/commands.fetch.cpp
@@ -3,7 +3,7 @@
#include <vcpkg/base/archives.h>
#include <vcpkg/base/checks.h>
#include <vcpkg/base/downloads.h>
-#include <vcpkg/base/sortedvector.h>
+#include <vcpkg/base/stringrange.h>
#include <vcpkg/base/strings.h>
#include <vcpkg/base/system.h>
#include <vcpkg/base/util.h>
@@ -13,10 +13,6 @@
namespace vcpkg::Commands::Fetch
{
- static constexpr CStringView V_120 = "v120";
- static constexpr CStringView V_140 = "v140";
- static constexpr CStringView V_141 = "v141";
-
struct ToolData
{
std::array<int, 3> version;
@@ -46,87 +42,6 @@ namespace vcpkg::Commands::Fetch
return result;
}
- struct VcpkgStringRange
- {
- VcpkgStringRange() = default;
-
- // Implicit by design
- VcpkgStringRange(const std::string& s) : begin(s.cbegin()), end(s.cend()) {}
-
- VcpkgStringRange(const std::string::const_iterator begin, const std::string::const_iterator end)
- : begin(begin), end(end)
- {
- }
-
- std::string::const_iterator begin;
- std::string::const_iterator end;
-
- std::string to_string() const { return std::string(this->begin, this->end); }
- };
-
- static std::vector<VcpkgStringRange> find_all_enclosed(const VcpkgStringRange& input,
- const std::string& left_delim,
- const std::string& right_delim)
- {
- std::string::const_iterator it_left = input.begin;
- std::string::const_iterator it_right = input.begin;
-
- std::vector<VcpkgStringRange> output;
-
- while (true)
- {
- it_left = std::search(it_right, input.end, left_delim.cbegin(), left_delim.cend());
- if (it_left == input.end) break;
-
- it_left += left_delim.length();
-
- it_right = std::search(it_left, input.end, right_delim.cbegin(), right_delim.cend());
- if (it_right == input.end) break;
-
- output.emplace_back(it_left, it_right);
-
- ++it_right;
- }
-
- return output;
- }
-
- static VcpkgStringRange find_exactly_one_enclosed(const VcpkgStringRange& input,
- const std::string& left_tag,
- const std::string& right_tag)
- {
- std::vector<VcpkgStringRange> result = find_all_enclosed(input, left_tag, right_tag);
- Checks::check_exit(VCPKG_LINE_INFO,
- result.size() == 1,
- "Found %d sets of %s.*%s but expected exactly 1, in block:\n%s",
- result.size(),
- left_tag,
- right_tag,
- input);
- return std::move(result.front());
- }
-
- static Optional<VcpkgStringRange> find_at_most_one_enclosed(const VcpkgStringRange& input,
- const std::string& left_tag,
- const std::string& right_tag)
- {
- std::vector<VcpkgStringRange> result = find_all_enclosed(input, left_tag, right_tag);
- Checks::check_exit(VCPKG_LINE_INFO,
- result.size() <= 1,
- "Found %d sets of %s.*%s but expected at most 1, in block:\n%s",
- result.size(),
- left_tag,
- right_tag,
- input);
-
- if (result.empty())
- {
- return nullopt;
- }
-
- return result.front();
- }
-
static ToolData parse_tool_data_from_xml(const VcpkgPaths& paths, const std::string& tool)
{
#if defined(_WIN32)
@@ -167,14 +82,17 @@ namespace vcpkg::Commands::Fetch
tool,
XML_PATH.generic_string());
- const std::string tool_data = find_exactly_one_enclosed(XML, match_tool_entry[0], "</tool>").to_string();
+ const std::string tool_data =
+ VcpkgStringRange::find_exactly_one_enclosed(XML, match_tool_entry[0], "</tool>").to_string();
const std::string version_as_string =
- find_exactly_one_enclosed(tool_data, "<version>", "</version>").to_string();
+ VcpkgStringRange::find_exactly_one_enclosed(tool_data, "<version>", "</version>").to_string();
const std::string exe_relative_path =
- find_exactly_one_enclosed(tool_data, "<exeRelativePath>", "</exeRelativePath>").to_string();
- const std::string url = find_exactly_one_enclosed(tool_data, "<url>", "</url>").to_string();
- const std::string sha512 = find_exactly_one_enclosed(tool_data, "<sha512>", "</sha512>").to_string();
- auto archive_name = find_at_most_one_enclosed(tool_data, "<archiveName>", "</archiveName>");
+ VcpkgStringRange::find_exactly_one_enclosed(tool_data, "<exeRelativePath>", "</exeRelativePath>")
+ .to_string();
+ const std::string url = VcpkgStringRange::find_exactly_one_enclosed(tool_data, "<url>", "</url>").to_string();
+ const std::string sha512 =
+ VcpkgStringRange::find_exactly_one_enclosed(tool_data, "<sha512>", "</sha512>").to_string();
+ auto archive_name = VcpkgStringRange::find_at_most_one_enclosed(tool_data, "<archiveName>", "</archiveName>");
const Optional<std::array<int, 3>> version = parse_version_string(version_as_string);
Checks::check_exit(VCPKG_LINE_INFO,
@@ -420,292 +338,6 @@ namespace vcpkg::Commands::Fetch
return fetch_tool(paths, "installerbase", TOOL_DATA);
}
- struct VisualStudioInstance
- {
- enum class ReleaseType
- {
- STABLE,
- PRERELEASE,
- LEGACY
- };
-
- static bool preferred_first_comparator(const VisualStudioInstance& left, const VisualStudioInstance& right)
- {
- const auto get_preference_weight = [](const ReleaseType& type) -> int {
- switch (type)
- {
- case ReleaseType::STABLE: return 3;
- case ReleaseType::PRERELEASE: return 2;
- case ReleaseType::LEGACY: return 1;
- default: Checks::unreachable(VCPKG_LINE_INFO);
- }
- };
-
- if (left.release_type != right.release_type)
- {
- return get_preference_weight(left.release_type) > get_preference_weight(right.release_type);
- }
-
- return left.version > right.version;
- }
-
- VisualStudioInstance(fs::path&& root_path, std::string&& version, const ReleaseType& release_type)
- : root_path(std::move(root_path)), version(std::move(version)), release_type(release_type)
- {
- }
-
- fs::path root_path;
- std::string version;
- ReleaseType release_type;
-
- std::string major_version() const { return version.substr(0, 2); }
- };
-
-#if defined(WIN32)
- static std::vector<VisualStudioInstance> get_visual_studio_instances(const VcpkgPaths& paths)
- {
- const auto& fs = paths.get_filesystem();
- std::vector<VisualStudioInstance> instances;
-
- const auto& program_files_32_bit = System::get_program_files_32_bit().value_or_exit(VCPKG_LINE_INFO);
-
- // Instances from vswhere
- const fs::path vswhere_exe = program_files_32_bit / "Microsoft Visual Studio" / "Installer" / "vswhere.exe";
- if (fs.exists(vswhere_exe))
- {
- const auto code_and_output = System::cmd_execute_and_capture_output(
- Strings::format(R"("%s" -prerelease -legacy -products * -format xml)", vswhere_exe.u8string()));
- Checks::check_exit(VCPKG_LINE_INFO,
- code_and_output.exit_code == 0,
- "Running vswhere.exe failed with message:\n%s",
- code_and_output.output);
-
- const auto instance_entries = find_all_enclosed(code_and_output.output, "<instance>", "</instance>");
- for (const VcpkgStringRange& instance : instance_entries)
- {
- auto maybe_is_prerelease = find_at_most_one_enclosed(instance, "<isPrerelease>", "</isPrerelease>");
-
- VisualStudioInstance::ReleaseType release_type = VisualStudioInstance::ReleaseType::LEGACY;
- if (const auto p = maybe_is_prerelease.get())
- {
- const auto s = p->to_string();
- if (s == "0")
- release_type = VisualStudioInstance::ReleaseType::STABLE;
- else if (s == "1")
- release_type = VisualStudioInstance::ReleaseType::PRERELEASE;
- else
- Checks::unreachable(VCPKG_LINE_INFO);
- }
-
- instances.emplace_back(
- find_exactly_one_enclosed(instance, "<installationPath>", "</installationPath>").to_string(),
- find_exactly_one_enclosed(instance, "<installationVersion>", "</installationVersion>").to_string(),
- release_type);
- }
- }
-
- const auto append_if_has_cl = [&](fs::path&& path_root) {
- const auto cl_exe = path_root / "VC" / "bin" / "cl.exe";
- const auto vcvarsall_bat = path_root / "VC" / "vcvarsall.bat";
-
- if (fs.exists(cl_exe) && fs.exists(vcvarsall_bat))
- instances.emplace_back(std::move(path_root), "14.0", VisualStudioInstance::ReleaseType::LEGACY);
- };
-
- // VS2015 instance from environment variable
- auto maybe_vs140_comntools = System::get_environment_variable("vs140comntools");
- if (const auto path_as_string = maybe_vs140_comntools.get())
- {
- // We want lexically_normal(), but it is not available
- // Correct root path might be 2 or 3 levels up, depending on if the path has trailing backslash. Try both.
- auto common7_tools = fs::path{*path_as_string};
- append_if_has_cl(fs::path{*path_as_string}.parent_path().parent_path());
- append_if_has_cl(fs::path{*path_as_string}.parent_path().parent_path().parent_path());
- }
-
- // VS2015 instance from Program Files
- append_if_has_cl(program_files_32_bit / "Microsoft Visual Studio 14.0");
-
- return instances;
- }
-#endif
-
-#if defined(_WIN32)
- std::vector<Toolset> find_toolset_instances_preferred_first(const VcpkgPaths& paths)
- {
- using CPU = System::CPUArchitecture;
-
- const auto& fs = paths.get_filesystem();
-
- // Note: this will contain a mix of vcvarsall.bat locations and dumpbin.exe locations.
- std::vector<fs::path> paths_examined;
-
- std::vector<Toolset> found_toolsets;
- std::vector<Toolset> excluded_toolsets;
-
- const SortedVector<VisualStudioInstance> sorted{get_visual_studio_instances(paths),
- VisualStudioInstance::preferred_first_comparator};
-
- const bool v140_is_available = Util::find_if(sorted, [&](const VisualStudioInstance& vs_instance) {
- return vs_instance.major_version() == "14";
- }) != sorted.end();
-
- for (const VisualStudioInstance& vs_instance : sorted)
- {
- const std::string major_version = vs_instance.major_version();
- if (major_version == "15")
- {
- const fs::path vc_dir = vs_instance.root_path / "VC";
-
- // Skip any instances that do not have vcvarsall.
- const fs::path vcvarsall_dir = vc_dir / "Auxiliary" / "Build";
- const fs::path vcvarsall_bat = vcvarsall_dir / "vcvarsall.bat";
- paths_examined.push_back(vcvarsall_bat);
- if (!fs.exists(vcvarsall_bat)) continue;
-
- // Get all supported architectures
- std::vector<ToolsetArchOption> supported_architectures;
- if (fs.exists(vcvarsall_dir / "vcvars32.bat"))
- supported_architectures.push_back({"x86", CPU::X86, CPU::X86});
- if (fs.exists(vcvarsall_dir / "vcvars64.bat"))
- supported_architectures.push_back({"amd64", CPU::X64, CPU::X64});
- if (fs.exists(vcvarsall_dir / "vcvarsx86_amd64.bat"))
- supported_architectures.push_back({"x86_amd64", CPU::X86, CPU::X64});
- if (fs.exists(vcvarsall_dir / "vcvarsx86_arm.bat"))
- supported_architectures.push_back({"x86_arm", CPU::X86, CPU::ARM});
- if (fs.exists(vcvarsall_dir / "vcvarsx86_arm64.bat"))
- supported_architectures.push_back({"x86_arm64", CPU::X86, CPU::ARM64});
- if (fs.exists(vcvarsall_dir / "vcvarsamd64_x86.bat"))
- supported_architectures.push_back({"amd64_x86", CPU::X64, CPU::X86});
- if (fs.exists(vcvarsall_dir / "vcvarsamd64_arm.bat"))
- supported_architectures.push_back({"amd64_arm", CPU::X64, CPU::ARM});
- if (fs.exists(vcvarsall_dir / "vcvarsamd64_arm64.bat"))
- supported_architectures.push_back({"amd64_arm64", CPU::X64, CPU::ARM64});
-
- // Locate the "best" MSVC toolchain version
- const fs::path msvc_path = vc_dir / "Tools" / "MSVC";
- std::vector<fs::path> msvc_subdirectories = fs.get_files_non_recursive(msvc_path);
- Util::unstable_keep_if(msvc_subdirectories,
- [&fs](const fs::path& path) { return fs.is_directory(path); });
-
- // Sort them so that latest comes first
- std::sort(
- msvc_subdirectories.begin(),
- msvc_subdirectories.end(),
- [](const fs::path& left, const fs::path& right) { return left.filename() > right.filename(); });
-
- for (const fs::path& subdir : msvc_subdirectories)
- {
- const fs::path dumpbin_path = subdir / "bin" / "HostX86" / "x86" / "dumpbin.exe";
- paths_examined.push_back(dumpbin_path);
- if (fs.exists(dumpbin_path))
- {
- const Toolset v141toolset{
- vs_instance.root_path, dumpbin_path, vcvarsall_bat, {}, V_141, supported_architectures};
-
- auto english_language_pack = dumpbin_path.parent_path() / "1033";
-
- if (!fs.exists(english_language_pack))
- {
- excluded_toolsets.push_back(v141toolset);
- break;
- }
-
- found_toolsets.push_back(v141toolset);
-
- if (v140_is_available)
- {
- const Toolset v140toolset{vs_instance.root_path,
- dumpbin_path,
- vcvarsall_bat,
- {"-vcvars_ver=14.0"},
- V_140,
- supported_architectures};
- found_toolsets.push_back(v140toolset);
- }
-
- break;
- }
- }
-
- continue;
- }
-
- if (major_version == "14" || major_version == "12")
- {
- const fs::path vcvarsall_bat = vs_instance.root_path / "VC" / "vcvarsall.bat";
-
- paths_examined.push_back(vcvarsall_bat);
- if (fs.exists(vcvarsall_bat))
- {
- const fs::path vs_dumpbin_exe = vs_instance.root_path / "VC" / "bin" / "dumpbin.exe";
- paths_examined.push_back(vs_dumpbin_exe);
-
- const fs::path vs_bin_dir = vcvarsall_bat.parent_path() / "bin";
- std::vector<ToolsetArchOption> supported_architectures;
- if (fs.exists(vs_bin_dir / "vcvars32.bat"))
- supported_architectures.push_back({"x86", CPU::X86, CPU::X86});
- if (fs.exists(vs_bin_dir / "amd64\\vcvars64.bat"))
- supported_architectures.push_back({"x64", CPU::X64, CPU::X64});
- if (fs.exists(vs_bin_dir / "x86_amd64\\vcvarsx86_amd64.bat"))
- supported_architectures.push_back({"x86_amd64", CPU::X86, CPU::X64});
- if (fs.exists(vs_bin_dir / "x86_arm\\vcvarsx86_arm.bat"))
- supported_architectures.push_back({"x86_arm", CPU::X86, CPU::ARM});
- if (fs.exists(vs_bin_dir / "amd64_x86\\vcvarsamd64_x86.bat"))
- supported_architectures.push_back({"amd64_x86", CPU::X64, CPU::X86});
- if (fs.exists(vs_bin_dir / "amd64_arm\\vcvarsamd64_arm.bat"))
- supported_architectures.push_back({"amd64_arm", CPU::X64, CPU::ARM});
-
- if (fs.exists(vs_dumpbin_exe))
- {
- const Toolset toolset = {vs_instance.root_path,
- vs_dumpbin_exe,
- vcvarsall_bat,
- {},
- major_version == "14" ? V_140 : V_120,
- supported_architectures};
-
- auto english_language_pack = vs_dumpbin_exe.parent_path() / "1033";
-
- if (!fs.exists(english_language_pack))
- {
- excluded_toolsets.push_back(toolset);
- break;
- }
-
- found_toolsets.push_back(toolset);
- }
- }
- }
- }
-
- if (!excluded_toolsets.empty())
- {
- System::println(
- System::Color::warning,
- "Warning: The following VS instances are excluded because the English language pack is unavailable.");
- for (const Toolset& toolset : excluded_toolsets)
- {
- System::println(" %s", toolset.visual_studio_root_path.u8string());
- }
- System::println(System::Color::warning, "Please install the English language pack.");
- }
-
- if (found_toolsets.empty())
- {
- System::println(System::Color::error, "Could not locate a complete toolset.");
- System::println("The following paths were examined:");
- for (const fs::path& path : paths_examined)
- {
- System::println(" %s", path.u8string());
- }
- Checks::exit_fail(VCPKG_LINE_INFO);
- }
-
- return found_toolsets;
- }
-#endif
-
fs::path get_tool_path(const VcpkgPaths& paths, const std::string& tool)
{
// First deal with specially handled tools.
diff --git a/toolsrc/src/vcpkg/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp
index 9b74bea74..f98e51764 100644
--- a/toolsrc/src/vcpkg/vcpkgpaths.cpp
+++ b/toolsrc/src/vcpkg/vcpkgpaths.cpp
@@ -9,6 +9,7 @@
#include <vcpkg/metrics.h>
#include <vcpkg/packagespec.h>
#include <vcpkg/vcpkgpaths.h>
+#include <vcpkg/visualstudio.h>
namespace vcpkg
{
@@ -117,8 +118,8 @@ namespace vcpkg
#if !defined(_WIN32)
Checks::exit_with_message(VCPKG_LINE_INFO, "Cannot build windows triplets from non-windows.");
#else
- const std::vector<Toolset>& vs_toolsets = this->toolsets.get_lazy(
- [this]() { return Commands::Fetch::find_toolset_instances_preferred_first(*this); });
+ const std::vector<Toolset>& vs_toolsets =
+ this->toolsets.get_lazy([this]() { return VisualStudio::find_toolset_instances_preferred_first(*this); });
std::vector<const Toolset*> candidates = Util::element_pointers(vs_toolsets);
const auto tsv = prebuildinfo.platform_toolset.get();
diff --git a/toolsrc/src/vcpkg/visualstudio.cpp b/toolsrc/src/vcpkg/visualstudio.cpp
new file mode 100644
index 000000000..d5f801f28
--- /dev/null
+++ b/toolsrc/src/vcpkg/visualstudio.cpp
@@ -0,0 +1,304 @@
+#include "pch.h"
+
+#if defined(_WIN32)
+
+#include <vcpkg/base/sortedvector.h>
+#include <vcpkg/base/stringrange.h>
+#include <vcpkg/base/util.h>
+#include <vcpkg/visualstudio.h>
+
+namespace vcpkg::VisualStudio
+{
+ static constexpr CStringView V_120 = "v120";
+ static constexpr CStringView V_140 = "v140";
+ static constexpr CStringView V_141 = "v141";
+
+ struct VisualStudioInstance
+ {
+ enum class ReleaseType
+ {
+ STABLE,
+ PRERELEASE,
+ LEGACY
+ };
+
+ static bool preferred_first_comparator(const VisualStudioInstance& left, const VisualStudioInstance& right)
+ {
+ const auto get_preference_weight = [](const ReleaseType& type) -> int {
+ switch (type)
+ {
+ case ReleaseType::STABLE: return 3;
+ case ReleaseType::PRERELEASE: return 2;
+ case ReleaseType::LEGACY: return 1;
+ default: Checks::unreachable(VCPKG_LINE_INFO);
+ }
+ };
+
+ if (left.release_type != right.release_type)
+ {
+ return get_preference_weight(left.release_type) > get_preference_weight(right.release_type);
+ }
+
+ return left.version > right.version;
+ }
+
+ VisualStudioInstance(fs::path&& root_path, std::string&& version, const ReleaseType& release_type)
+ : root_path(std::move(root_path)), version(std::move(version)), release_type(release_type)
+ {
+ }
+
+ fs::path root_path;
+ std::string version;
+ ReleaseType release_type;
+
+ std::string major_version() const { return version.substr(0, 2); }
+ };
+
+ static std::vector<VisualStudioInstance> get_visual_studio_instances(const VcpkgPaths& paths)
+ {
+ const auto& fs = paths.get_filesystem();
+ std::vector<VisualStudioInstance> instances;
+
+ const auto& program_files_32_bit = System::get_program_files_32_bit().value_or_exit(VCPKG_LINE_INFO);
+
+ // Instances from vswhere
+ const fs::path vswhere_exe = program_files_32_bit / "Microsoft Visual Studio" / "Installer" / "vswhere.exe";
+ if (fs.exists(vswhere_exe))
+ {
+ const auto code_and_output = System::cmd_execute_and_capture_output(
+ Strings::format(R"("%s" -prerelease -legacy -products * -format xml)", vswhere_exe.u8string()));
+ Checks::check_exit(VCPKG_LINE_INFO,
+ code_and_output.exit_code == 0,
+ "Running vswhere.exe failed with message:\n%s",
+ code_and_output.output);
+
+ const auto instance_entries =
+ VcpkgStringRange::find_all_enclosed(code_and_output.output, "<instance>", "</instance>");
+ for (const VcpkgStringRange& instance : instance_entries)
+ {
+ auto maybe_is_prerelease =
+ VcpkgStringRange::find_at_most_one_enclosed(instance, "<isPrerelease>", "</isPrerelease>");
+
+ VisualStudioInstance::ReleaseType release_type = VisualStudioInstance::ReleaseType::LEGACY;
+ if (const auto p = maybe_is_prerelease.get())
+ {
+ const auto s = p->to_string();
+ if (s == "0")
+ release_type = VisualStudioInstance::ReleaseType::STABLE;
+ else if (s == "1")
+ release_type = VisualStudioInstance::ReleaseType::PRERELEASE;
+ else
+ Checks::unreachable(VCPKG_LINE_INFO);
+ }
+
+ instances.emplace_back(
+ VcpkgStringRange::find_exactly_one_enclosed(instance, "<installationPath>", "</installationPath>")
+ .to_string(),
+ VcpkgStringRange::find_exactly_one_enclosed(
+ instance, "<installationVersion>", "</installationVersion>")
+ .to_string(),
+ release_type);
+ }
+ }
+
+ const auto append_if_has_cl = [&](fs::path&& path_root) {
+ const auto cl_exe = path_root / "VC" / "bin" / "cl.exe";
+ const auto vcvarsall_bat = path_root / "VC" / "vcvarsall.bat";
+
+ if (fs.exists(cl_exe) && fs.exists(vcvarsall_bat))
+ instances.emplace_back(std::move(path_root), "14.0", VisualStudioInstance::ReleaseType::LEGACY);
+ };
+
+ // VS2015 instance from environment variable
+ auto maybe_vs140_comntools = System::get_environment_variable("vs140comntools");
+ if (const auto path_as_string = maybe_vs140_comntools.get())
+ {
+ // We want lexically_normal(), but it is not available
+ // Correct root path might be 2 or 3 levels up, depending on if the path has trailing backslash. Try both.
+ auto common7_tools = fs::path{*path_as_string};
+ append_if_has_cl(fs::path{*path_as_string}.parent_path().parent_path());
+ append_if_has_cl(fs::path{*path_as_string}.parent_path().parent_path().parent_path());
+ }
+
+ // VS2015 instance from Program Files
+ append_if_has_cl(program_files_32_bit / "Microsoft Visual Studio 14.0");
+
+ return instances;
+ }
+
+ std::vector<Toolset> find_toolset_instances_preferred_first(const VcpkgPaths& paths)
+ {
+ using CPU = System::CPUArchitecture;
+
+ const auto& fs = paths.get_filesystem();
+
+ // Note: this will contain a mix of vcvarsall.bat locations and dumpbin.exe locations.
+ std::vector<fs::path> paths_examined;
+
+ std::vector<Toolset> found_toolsets;
+ std::vector<Toolset> excluded_toolsets;
+
+ const SortedVector<VisualStudioInstance> sorted{get_visual_studio_instances(paths),
+ VisualStudioInstance::preferred_first_comparator};
+
+ const bool v140_is_available = Util::find_if(sorted, [&](const VisualStudioInstance& vs_instance) {
+ return vs_instance.major_version() == "14";
+ }) != sorted.end();
+
+ for (const VisualStudioInstance& vs_instance : sorted)
+ {
+ const std::string major_version = vs_instance.major_version();
+ if (major_version == "15")
+ {
+ const fs::path vc_dir = vs_instance.root_path / "VC";
+
+ // Skip any instances that do not have vcvarsall.
+ const fs::path vcvarsall_dir = vc_dir / "Auxiliary" / "Build";
+ const fs::path vcvarsall_bat = vcvarsall_dir / "vcvarsall.bat";
+ paths_examined.push_back(vcvarsall_bat);
+ if (!fs.exists(vcvarsall_bat)) continue;
+
+ // Get all supported architectures
+ std::vector<ToolsetArchOption> supported_architectures;
+ if (fs.exists(vcvarsall_dir / "vcvars32.bat"))
+ supported_architectures.push_back({"x86", CPU::X86, CPU::X86});
+ if (fs.exists(vcvarsall_dir / "vcvars64.bat"))
+ supported_architectures.push_back({"amd64", CPU::X64, CPU::X64});
+ if (fs.exists(vcvarsall_dir / "vcvarsx86_amd64.bat"))
+ supported_architectures.push_back({"x86_amd64", CPU::X86, CPU::X64});
+ if (fs.exists(vcvarsall_dir / "vcvarsx86_arm.bat"))
+ supported_architectures.push_back({"x86_arm", CPU::X86, CPU::ARM});
+ if (fs.exists(vcvarsall_dir / "vcvarsx86_arm64.bat"))
+ supported_architectures.push_back({"x86_arm64", CPU::X86, CPU::ARM64});
+ if (fs.exists(vcvarsall_dir / "vcvarsamd64_x86.bat"))
+ supported_architectures.push_back({"amd64_x86", CPU::X64, CPU::X86});
+ if (fs.exists(vcvarsall_dir / "vcvarsamd64_arm.bat"))
+ supported_architectures.push_back({"amd64_arm", CPU::X64, CPU::ARM});
+ if (fs.exists(vcvarsall_dir / "vcvarsamd64_arm64.bat"))
+ supported_architectures.push_back({"amd64_arm64", CPU::X64, CPU::ARM64});
+
+ // Locate the "best" MSVC toolchain version
+ const fs::path msvc_path = vc_dir / "Tools" / "MSVC";
+ std::vector<fs::path> msvc_subdirectories = fs.get_files_non_recursive(msvc_path);
+ Util::unstable_keep_if(msvc_subdirectories,
+ [&fs](const fs::path& path) { return fs.is_directory(path); });
+
+ // Sort them so that latest comes first
+ std::sort(
+ msvc_subdirectories.begin(),
+ msvc_subdirectories.end(),
+ [](const fs::path& left, const fs::path& right) { return left.filename() > right.filename(); });
+
+ for (const fs::path& subdir : msvc_subdirectories)
+ {
+ const fs::path dumpbin_path = subdir / "bin" / "HostX86" / "x86" / "dumpbin.exe";
+ paths_examined.push_back(dumpbin_path);
+ if (fs.exists(dumpbin_path))
+ {
+ const Toolset v141_toolset{
+ vs_instance.root_path, dumpbin_path, vcvarsall_bat, {}, V_141, supported_architectures};
+
+ const auto english_language_pack = dumpbin_path.parent_path() / "1033";
+
+ if (!fs.exists(english_language_pack))
+ {
+ excluded_toolsets.push_back(v141_toolset);
+ break;
+ }
+
+ found_toolsets.push_back(v141_toolset);
+
+ if (v140_is_available)
+ {
+ const Toolset v140_toolset{vs_instance.root_path,
+ dumpbin_path,
+ vcvarsall_bat,
+ {"-vcvars_ver=14.0"},
+ V_140,
+ supported_architectures};
+ found_toolsets.push_back(v140_toolset);
+ }
+
+ break;
+ }
+ }
+
+ continue;
+ }
+
+ if (major_version == "14" || major_version == "12")
+ {
+ const fs::path vcvarsall_bat = vs_instance.root_path / "VC" / "vcvarsall.bat";
+
+ paths_examined.push_back(vcvarsall_bat);
+ if (fs.exists(vcvarsall_bat))
+ {
+ const fs::path vs_dumpbin_exe = vs_instance.root_path / "VC" / "bin" / "dumpbin.exe";
+ paths_examined.push_back(vs_dumpbin_exe);
+
+ const fs::path vs_bin_dir = vcvarsall_bat.parent_path() / "bin";
+ std::vector<ToolsetArchOption> supported_architectures;
+ if (fs.exists(vs_bin_dir / "vcvars32.bat"))
+ supported_architectures.push_back({"x86", CPU::X86, CPU::X86});
+ if (fs.exists(vs_bin_dir / "amd64\\vcvars64.bat"))
+ supported_architectures.push_back({"x64", CPU::X64, CPU::X64});
+ if (fs.exists(vs_bin_dir / "x86_amd64\\vcvarsx86_amd64.bat"))
+ supported_architectures.push_back({"x86_amd64", CPU::X86, CPU::X64});
+ if (fs.exists(vs_bin_dir / "x86_arm\\vcvarsx86_arm.bat"))
+ supported_architectures.push_back({"x86_arm", CPU::X86, CPU::ARM});
+ if (fs.exists(vs_bin_dir / "amd64_x86\\vcvarsamd64_x86.bat"))
+ supported_architectures.push_back({"amd64_x86", CPU::X64, CPU::X86});
+ if (fs.exists(vs_bin_dir / "amd64_arm\\vcvarsamd64_arm.bat"))
+ supported_architectures.push_back({"amd64_arm", CPU::X64, CPU::ARM});
+
+ if (fs.exists(vs_dumpbin_exe))
+ {
+ const Toolset toolset = {vs_instance.root_path,
+ vs_dumpbin_exe,
+ vcvarsall_bat,
+ {},
+ major_version == "14" ? V_140 : V_120,
+ supported_architectures};
+
+ const auto english_language_pack = vs_dumpbin_exe.parent_path() / "1033";
+
+ if (!fs.exists(english_language_pack))
+ {
+ excluded_toolsets.push_back(toolset);
+ break;
+ }
+
+ found_toolsets.push_back(toolset);
+ }
+ }
+ }
+ }
+
+ if (!excluded_toolsets.empty())
+ {
+ System::println(
+ System::Color::warning,
+ "Warning: The following VS instances are excluded because the English language pack is unavailable.");
+ for (const Toolset& toolset : excluded_toolsets)
+ {
+ System::println(" %s", toolset.visual_studio_root_path.u8string());
+ }
+ System::println(System::Color::warning, "Please install the English language pack.");
+ }
+
+ if (found_toolsets.empty())
+ {
+ System::println(System::Color::error, "Could not locate a complete toolset.");
+ System::println("The following paths were examined:");
+ for (const fs::path& path : paths_examined)
+ {
+ System::println(" %s", path.u8string());
+ }
+ Checks::exit_fail(VCPKG_LINE_INFO);
+ }
+
+ return found_toolsets;
+ }
+}
+
+#endif
diff --git a/toolsrc/vcpkglib/vcpkglib.vcxproj b/toolsrc/vcpkglib/vcpkglib.vcxproj
index 7e825b513..d70cd6eab 100644
--- a/toolsrc/vcpkglib/vcpkglib.vcxproj
+++ b/toolsrc/vcpkglib/vcpkglib.vcxproj
@@ -154,6 +154,7 @@
<ClInclude Include="..\include\vcpkg\base\sortedvector.h" />
<ClInclude Include="..\include\vcpkg\base\span.h" />
<ClInclude Include="..\include\vcpkg\base\stringliteral.h" />
+ <ClInclude Include="..\include\vcpkg\base\stringrange.h" />
<ClInclude Include="..\include\vcpkg\base\strings.h" />
<ClInclude Include="..\include\vcpkg\base\system.h" />
<ClInclude Include="..\include\vcpkg\base\util.h" />
@@ -186,6 +187,7 @@
<ClInclude Include="..\include\vcpkg\vcpkglib.h" />
<ClInclude Include="..\include\vcpkg\vcpkgpaths.h" />
<ClInclude Include="..\include\vcpkg\versiont.h" />
+ <ClInclude Include="..\include\vcpkg\visualstudio.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\pch.cpp">
@@ -203,6 +205,7 @@
<ClCompile Include="..\src\vcpkg\base\files.cpp" />
<ClCompile Include="..\src\vcpkg\base\lineinfo.cpp" />
<ClCompile Include="..\src\vcpkg\base\machinetype.cpp" />
+ <ClCompile Include="..\src\vcpkg\base\stringrange.cpp" />
<ClCompile Include="..\src\vcpkg\base\strings.cpp" />
<ClCompile Include="..\src\vcpkg\base\system.cpp" />
<ClCompile Include="..\src\vcpkg\binaryparagraph.cpp" />
@@ -253,6 +256,7 @@
<ClCompile Include="..\src\vcpkg\vcpkglib.cpp" />
<ClCompile Include="..\src\vcpkg\vcpkgpaths.cpp" />
<ClCompile Include="..\src\vcpkg\versiont.cpp" />
+ <ClCompile Include="..\src\vcpkg\visualstudio.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
diff --git a/toolsrc/vcpkglib/vcpkglib.vcxproj.filters b/toolsrc/vcpkglib/vcpkglib.vcxproj.filters
index d4e179d19..df801e85b 100644
--- a/toolsrc/vcpkglib/vcpkglib.vcxproj.filters
+++ b/toolsrc/vcpkglib/vcpkglib.vcxproj.filters
@@ -207,6 +207,12 @@
<ClCompile Include="..\src\vcpkg\base\archives.cpp">
<Filter>Source Files\vcpkg\base</Filter>
</ClCompile>
+ <ClCompile Include="..\src\vcpkg\visualstudio.cpp">
+ <Filter>Source Files\vcpkg</Filter>
+ </ClCompile>
+ <ClCompile Include="..\src\vcpkg\base\stringrange.cpp">
+ <Filter>Source Files\vcpkg\base</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\pch.h">
@@ -359,5 +365,11 @@
<ClInclude Include="..\include\vcpkg\base\archives.h">
<Filter>Header Files\vcpkg\base</Filter>
</ClInclude>
+ <ClInclude Include="..\include\vcpkg\visualstudio.h">
+ <Filter>Header Files\vcpkg\base</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\vcpkg\base\stringrange.h">
+ <Filter>Header Files\vcpkg\base</Filter>
+ </ClInclude>
</ItemGroup>
</Project> \ No newline at end of file