diff options
| author | Alexander Karatarakis <alkarata@microsoft.com> | 2018-06-19 17:51:20 -0700 |
|---|---|---|
| committer | Alexander Karatarakis <alkarata@microsoft.com> | 2018-06-19 23:07:31 -0700 |
| commit | c256ccf4526decec0422d8fc9d91ceb8fcf6160b (patch) | |
| tree | e1b90d4fbaebfb8ae794659ee4cd223046e8056e | |
| parent | dbae3bfe566c4c2d949ca6b2f84c6867f15e46ac (diff) | |
| download | vcpkg-c256ccf4526decec0422d8fc9d91ceb8fcf6160b.tar.gz vcpkg-c256ccf4526decec0422d8fc9d91ceb8fcf6160b.zip | |
Introduce stringrange.h/cpp and visualstudio.h/cpp
| -rw-r--r-- | toolsrc/include/vcpkg/base/stringrange.h | 33 | ||||
| -rw-r--r-- | toolsrc/include/vcpkg/commands.h | 1 | ||||
| -rw-r--r-- | toolsrc/include/vcpkg/visualstudio.h | 12 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/base/stringrange.cpp | 79 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/commands.fetch.cpp | 388 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/vcpkgpaths.cpp | 5 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/visualstudio.cpp | 304 | ||||
| -rw-r--r-- | toolsrc/vcpkglib/vcpkglib.vcxproj | 4 | ||||
| -rw-r--r-- | toolsrc/vcpkglib/vcpkglib.vcxproj.filters | 12 |
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 |
