mirror of
https://github.com/microsoft/vcpkg.git
synced 2025-06-07 13:16:17 +08:00
Introduce stringrange.h/cpp and visualstudio.h/cpp
This commit is contained in:
parent
dbae3bfe56
commit
c256ccf452
33
toolsrc/include/vcpkg/base/stringrange.h
Normal file
33
toolsrc/include/vcpkg/base/stringrange.h
Normal file
@ -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;
|
||||
};
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
12
toolsrc/include/vcpkg/visualstudio.h
Normal file
12
toolsrc/include/vcpkg/visualstudio.h
Normal file
@ -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
|
79
toolsrc/src/vcpkg/base/stringrange.cpp
Normal file
79
toolsrc/src/vcpkg/base/stringrange.cpp
Normal file
@ -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); }
|
||||
}
|
@ -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.
|
||||
|
@ -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();
|
||||
|
304
toolsrc/src/vcpkg/visualstudio.cpp
Normal file
304
toolsrc/src/vcpkg/visualstudio.cpp
Normal file
@ -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
|
@ -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">
|
||||
|
@ -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>
|
Loading…
Reference in New Issue
Block a user