Introduce "vcpkg fetch"

This commit is contained in:
Alexander Karatarakis 2018-04-04 01:45:45 -07:00
parent 54c68da907
commit 0c0f68939e
13 changed files with 714 additions and 681 deletions

View File

@ -135,6 +135,13 @@ namespace vcpkg::Commands
std::string get_file_hash(const VcpkgPaths& paths, fs::path const& path, std::string const& hash_type);
}
namespace Fetch
{
std::vector<Toolset> find_toolset_instances(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);
}
template<class T>
struct PackageNameAndFunction
{

View File

@ -7,8 +7,22 @@
#include <vcpkg/base/files.h>
#include <vcpkg/base/lazy.h>
#include <map>
namespace vcpkg
{
namespace Tools
{
static const std::string SEVEN_ZIP = "7zip";
static const std::string CMAKE = "cmake";
static const std::string GIT = "git";
static const std::string NINJA = "ninja";
static const std::string NUGET = "nuget";
static const std::string IFW_INSTALLER_BASE = "ifw_installerbase";
static const std::string IFW_BINARYCREATOR = "ifw_binarycreator";
static const std::string IFW_REPOGEN = "ifw_repogen";
}
struct ToolsetArchOption
{
CStringView name;
@ -63,14 +77,7 @@ namespace vcpkg
fs::path ports_cmake;
const fs::path& get_7za_exe() const;
const fs::path& get_cmake_exe() const;
const fs::path& get_git_exe() const;
const fs::path& get_ninja_exe() const;
const fs::path& get_nuget_exe() const;
const fs::path& get_ifw_installerbase_exe() const;
const fs::path& get_ifw_binarycreator_exe() const;
const fs::path& get_ifw_repogen_exe() const;
const fs::path& get_tool_exe(const std::string& tool) const;
/// <summary>Retrieve a toolset matching a VS version</summary>
/// <remarks>
@ -82,14 +89,7 @@ namespace vcpkg
private:
Lazy<std::vector<std::string>> available_triplets;
Lazy<fs::path> _7za_exe;
Lazy<fs::path> cmake_exe;
Lazy<fs::path> git_exe;
Lazy<fs::path> ninja_exe;
Lazy<fs::path> nuget_exe;
Lazy<fs::path> ifw_installerbase_exe;
Lazy<fs::path> ifw_binarycreator_exe;
Lazy<fs::path> ifw_repogen_exe;
mutable std::map<std::string, fs::path> tool_paths;
Lazy<std::vector<Toolset>> toolsets;
Lazy<std::vector<Toolset>> toolsets_vs2013;

View File

@ -341,8 +341,8 @@ namespace vcpkg::Build
vcpkg::Util::unused(paths.get_ninja_exe());
#endif
const fs::path& cmake_exe_path = paths.get_cmake_exe();
const fs::path& git_exe_path = paths.get_git_exe();
const fs::path& cmake_exe_path = paths.get_tool_exe(Tools::CMAKE);
const fs::path& git_exe_path = paths.get_tool_exe(Tools::GIT);
std::string all_features;
for (auto& feature : config.scf.feature_paragraphs)
@ -361,9 +361,8 @@ namespace vcpkg::Build
{"TARGET_TRIPLET", spec.triplet().canonical_name()},
{"VCPKG_PLATFORM_TOOLSET", toolset.version.c_str()},
{"VCPKG_USE_HEAD_VERSION",
Util::Enum::to_bool(config.build_package_options.use_head_version) ? "1" : "0"},
{"_VCPKG_NO_DOWNLOADS",
!Util::Enum::to_bool(config.build_package_options.allow_downloads) ? "1" : "0"},
Util::Enum::to_bool(config.build_package_options.use_head_version) ? "1" : "0"},
{"_VCPKG_NO_DOWNLOADS", !Util::Enum::to_bool(config.build_package_options.allow_downloads) ? "1" : "0"},
{"_VCPKG_DOWNLOAD_TOOL", to_string(config.build_package_options.download_tool)},
{"GIT", git_exe_path},
{"FEATURES", Strings::join(";", config.feature_list)},
@ -519,7 +518,7 @@ namespace vcpkg::Build
Checks::check_exit(VCPKG_LINE_INFO, files.empty(), "unable to clear path: %s", pkg_path.u8string());
#if defined(_WIN32)
auto&& _7za = paths.get_7za_exe();
auto&& _7za = paths.get_tool_exe(Tools::SEVEN_ZIP);
System::cmd_execute_clean(Strings::format(
R"("%s" x "%s" -o"%s" -y >nul)", _7za.u8string(), archive_path.u8string(), pkg_path.u8string()));
@ -539,7 +538,7 @@ namespace vcpkg::Build
Checks::check_exit(
VCPKG_LINE_INFO, !fs.exists(tmp_archive_path), "Could not remove file: %s", tmp_archive_path.u8string());
#if defined(_WIN32)
auto&& _7za = paths.get_7za_exe();
auto&& _7za = paths.get_tool_exe(Tools::SEVEN_ZIP);
System::cmd_execute_clean(Strings::format(
R"("%s" a "%s" "%s\*" >nul)",
@ -771,7 +770,7 @@ namespace vcpkg::Build
{
static constexpr CStringView FLAG_GUID = "c35112b6-d1ba-415b-aa5d-81de856ef8eb";
const fs::path& cmake_exe_path = paths.get_cmake_exe();
const fs::path& cmake_exe_path = paths.get_tool_exe(Tools::CMAKE);
const fs::path ports_cmake_script_path = paths.scripts / "get_triplet_environment.cmake";
const fs::path triplet_file_path = paths.triplets / (triplet.canonical_name() + ".cmake");

View File

@ -43,16 +43,15 @@ namespace vcpkg::Commands
{"portsdiff", &PortsDiff::perform_and_exit},
{"autocomplete", &Autocomplete::perform_and_exit},
{"hash", &Hash::perform_and_exit},
};
{"fetch", &Fetch::perform_and_exit},
};
return t;
}
Span<const PackageNameAndFunction<CommandTypeC>> get_available_commands_type_c()
{
static std::vector<PackageNameAndFunction<CommandTypeC>> t = {
{"version", &Version::perform_and_exit},
{"contact", &Contact::perform_and_exit}
};
static std::vector<PackageNameAndFunction<CommandTypeC>> t = {{"version", &Version::perform_and_exit},
{"contact", &Contact::perform_and_exit}};
return t;
}
}

View File

@ -22,7 +22,7 @@ namespace vcpkg::Commands::Create
const std::string port_name = args.command_arguments.at(0);
const std::string url = args.command_arguments.at(1);
const fs::path& cmake_exe = paths.get_cmake_exe();
const fs::path& cmake_exe = paths.get_tool_exe(Tools::CMAKE);
std::vector<System::CMakeVariable> cmake_args{{"CMD", "CREATE"}, {"PORT", port_name}, {"URL", url}};

View File

@ -291,7 +291,7 @@ namespace vcpkg::Export::IFW
std::error_code ec;
Files::Filesystem& fs = paths.get_filesystem();
const fs::path& installerbase_exe = paths.get_ifw_installerbase_exe();
const fs::path& installerbase_exe = paths.get_tool_exe(Tools::IFW_INSTALLER_BASE);
fs::path tempmaintenancetool = ifw_packages_dir_path / "maintenance" / "data" / "tempmaintenancetool.exe";
fs.create_directories(tempmaintenancetool.parent_path(), ec);
Checks::check_exit(VCPKG_LINE_INFO,
@ -335,7 +335,7 @@ namespace vcpkg::Export::IFW
void do_repository(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths)
{
const fs::path& repogen_exe = paths.get_ifw_repogen_exe();
const fs::path& repogen_exe = paths.get_tool_exe(Tools::IFW_REPOGEN);
const fs::path packages_dir = get_packages_dir_path(export_id, ifw_options, paths);
const fs::path repository_dir = get_repository_dir_path(export_id, ifw_options, paths);
@ -361,7 +361,7 @@ namespace vcpkg::Export::IFW
void do_installer(const std::string& export_id, const Options& ifw_options, const VcpkgPaths& paths)
{
const fs::path& binarycreator_exe = paths.get_ifw_binarycreator_exe();
const fs::path& binarycreator_exe = paths.get_tool_exe(Tools::IFW_BINARYCREATOR);
const fs::path config_file = get_config_file_path(export_id, ifw_options, paths);
const fs::path packages_dir = get_packages_dir_path(export_id, ifw_options, paths);
const fs::path repository_dir = get_repository_dir_path(export_id, ifw_options, paths);

View File

@ -0,0 +1,656 @@
#include "pch.h"
#include <vcpkg/base/checks.h>
#include <vcpkg/base/strings.h>
#include <vcpkg/base/system.h>
#include <vcpkg/base/util.h>
#include <vcpkg/commands.h>
#include <vcpkg/help.h>
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> required_version;
fs::path exe_path;
std::string url;
fs::path downloaded_path;
fs::path tool_dir_path;
};
static Optional<std::array<int, 3>> parse_version_string(const std::string& version_as_string)
{
static const std::regex RE(R"###((\d+)\.(\d+)\.(\d+))###");
std::match_results<std::string::const_iterator> match;
const auto found = std::regex_search(version_as_string, match, RE);
if (!found)
{
return {};
}
const int d1 = atoi(match[1].str().c_str());
const int d2 = atoi(match[2].str().c_str());
const int d3 = atoi(match[3].str().c_str());
const std::array<int, 3> result = {d1, d2, d3};
return result;
}
static ToolData parse_tool_data_from_xml(const VcpkgPaths& paths, const std::string& tool)
{
#if defined(_WIN32)
static constexpr StringLiteral OS_STRING = "windows";
#elif defined(__APPLE__)
static constexpr StringLiteral OS_STRING = "osx";
#else // assume linux
static constexpr StringLiteral OS_STRING = "linux";
#endif
static const fs::path XML_PATH = paths.scripts / "vcpkgTools.xml";
const auto maybe_get_string_inside_tags = [](const std::string& input,
const std::regex& regex) -> Optional<std::string> {
std::smatch match;
const bool has_match = std::regex_search(input.cbegin(), input.cend(), match, regex);
if (!has_match) return nullopt;
return match[1];
};
const auto get_string_inside_tags =
[](const std::string& input, const std::regex& regex, const std::string& tag_name) -> std::string {
std::smatch match;
const bool has_match = std::regex_search(input.cbegin(), input.cend(), match, regex);
Checks::check_exit(
VCPKG_LINE_INFO, has_match, "Could not find tag <%s> in %s", tag_name, XML_PATH.generic_string());
return match[1];
};
static const std::string XML = paths.get_filesystem().read_contents(XML_PATH).value_or_exit(VCPKG_LINE_INFO);
static const std::regex VERSION_REGEX{R"###(<requiredVersion>([\s\S]*?)</requiredVersion>)###"};
static const std::regex EXE_RELATIVE_PATH_REGEX{
Strings::format(R"###(<exeRelativePath>([\s\S]*?)</exeRelativePath>)###")};
static const std::regex ARCHIVE_RELATIVE_PATH_REGEX{
Strings::format(R"###(<archiveRelativePath>([\s\S]*?)</archiveRelativePath>)###")};
static const std::regex URL_REGEX{Strings::format(R"###(<url>([\s\S]*?)</url>)###")};
std::regex tool_regex{
Strings::format(R"###(<tool[\s]+name="%s"[\s]+os="%s">([\s\S]*?)<\/tool>)###", tool, OS_STRING)};
std::smatch match_tool;
bool has_match_tool = std::regex_search(XML.cbegin(), XML.cend(), match_tool, tool_regex);
if (!has_match_tool && OS_STRING == "windows") // Legacy support. Change introduced in vcpkg v0.0.107.
{
tool_regex = Strings::format(R"###(<tool[\s]+name="%s">([\s\S]*?)<\/tool>)###", tool);
has_match_tool = std::regex_search(XML.cbegin(), XML.cend(), match_tool, tool_regex);
}
Checks::check_exit(VCPKG_LINE_INFO,
has_match_tool,
"Could not find entry for tool [%s] in %s",
tool,
XML_PATH.generic_string());
const std::string tool_data_as_string = get_string_inside_tags(XML, tool_regex, tool);
const std::string required_version_as_string =
get_string_inside_tags(tool_data_as_string, VERSION_REGEX, "requiredVersion");
const std::string url = get_string_inside_tags(tool_data_as_string, URL_REGEX, "url");
const std::string exe_relative_path =
get_string_inside_tags(tool_data_as_string, EXE_RELATIVE_PATH_REGEX, "exeRelativePath");
auto archive_relative_path = maybe_get_string_inside_tags(tool_data_as_string, ARCHIVE_RELATIVE_PATH_REGEX);
const Optional<std::array<int, 3>> required_version = parse_version_string(required_version_as_string);
Checks::check_exit(VCPKG_LINE_INFO,
required_version.has_value(),
"Could not parse version for tool %s. Version string was: %s",
tool,
required_version_as_string);
const std::string tool_dir_name = Strings::format("%s-%s-%s", tool, required_version_as_string, OS_STRING);
const fs::path tool_dir_path = paths.downloads / "tools" / tool_dir_name;
const fs::path exe_path = tool_dir_path / exe_relative_path;
return ToolData{*required_version.get(),
exe_path,
url,
paths.downloads / archive_relative_path.value_or(exe_relative_path),
tool_dir_path};
}
static bool exists_and_has_equal_or_greater_version(const std::string& version_cmd,
const std::array<int, 3>& expected_version)
{
const auto rc = System::cmd_execute_and_capture_output(Strings::format(R"(%s)", version_cmd));
if (rc.exit_code != 0)
{
return false;
}
const Optional<std::array<int, 3>> v = parse_version_string(rc.output);
if (!v.has_value())
{
return false;
}
const std::array<int, 3> actual_version = *v.get();
return (actual_version[0] > expected_version[0] ||
(actual_version[0] == expected_version[0] && actual_version[1] > expected_version[1]) ||
(actual_version[0] == expected_version[0] && actual_version[1] == expected_version[1] &&
actual_version[2] >= expected_version[2]));
}
static Optional<fs::path> find_if_has_equal_or_greater_version(const std::vector<fs::path>& candidate_paths,
const std::string& version_check_arguments,
const std::array<int, 3>& expected_version)
{
auto it = Util::find_if(candidate_paths, [&](const fs::path& p) {
const std::string cmd = Strings::format(R"("%s" %s)", p.u8string(), version_check_arguments);
return exists_and_has_equal_or_greater_version(cmd, expected_version);
});
if (it != candidate_paths.cend())
{
return std::move(*it);
}
return nullopt;
}
static std::vector<std::string> keep_data_lines(const std::string& data_blob)
{
static const std::regex DATA_LINE_REGEX(R"(<sol>::(.+?)(?=::<eol>))");
std::vector<std::string> data_lines;
const std::sregex_iterator it(data_blob.cbegin(), data_blob.cend(), DATA_LINE_REGEX);
const std::sregex_iterator end;
for (std::sregex_iterator i = it; i != end; ++i)
{
const std::smatch match = *i;
data_lines.push_back(match[1].str());
}
return data_lines;
}
static void extract_archive(const VcpkgPaths& paths, const fs::path& archive, const fs::path& to_path)
{
Files::Filesystem& fs = paths.get_filesystem();
const fs::path to_path_partial = to_path.u8string() + ".partial";
std::error_code ec;
fs.remove_all(to_path_partial, ec);
fs.create_directories(to_path_partial, ec);
const auto ext = archive.extension();
if (ext == ".gz" && ext.extension() != ".tar")
{
const auto code = System::cmd_execute(
Strings::format(R"(cd '%s' && tar xzf '%s')", to_path_partial.u8string(), archive.u8string()));
Checks::check_exit(VCPKG_LINE_INFO, code == 0, "tar failed while extracting %s", archive.u8string());
}
else if (ext == ".zip")
{
const auto code = System::cmd_execute(
Strings::format(R"(cd '%s' && unzip -qqo '%s')", to_path_partial.u8string(), archive.u8string()));
Checks::check_exit(VCPKG_LINE_INFO, code == 0, "unzip failed while extracting %s", archive.u8string());
}
else
{
Checks::exit_with_message(VCPKG_LINE_INFO, "Unexpected archive extension: %s", ext.u8string());
}
fs.rename(to_path_partial, to_path);
}
static fs::path fetch_tool(const VcpkgPaths& paths, const std::string& tool_name, const ToolData& tool_data)
{
const auto& fs = paths.get_filesystem();
const fs::path& scripts_folder = paths.scripts;
const std::array<int, 3>& version = tool_data.required_version;
const std::string version_as_string = Strings::format("%d.%d.%d", version[0], version[1], version[2]);
System::println("A suitable version of %s was not found (required v%s). Downloading portable %s v%s...",
tool_name,
version_as_string,
tool_name,
version_as_string);
#if defined(_WIN32)
const fs::path script = scripts_folder / "fetchtool.ps1";
const std::string title = Strings::format(
"Fetching %s version %s (No sufficient installed version was found)", tool_name, version_as_string);
const System::PowershellParameter tool_param("tool", tool_name);
const std::string output = System::powershell_execute_and_capture_output(title, script, {tool_param});
const std::vector<std::string> tool_path = keep_data_lines(output);
Checks::check_exit(VCPKG_LINE_INFO, tool_path.size() == 1, "Expected tool path, but got %s", output);
const fs::path actual_downloaded_path = Strings::trim(std::string{tool_path.at(0)});
const fs::path& expected_downloaded_path = tool_data.exe_path;
std::error_code ec;
const auto eq = fs::stdfs::equivalent(expected_downloaded_path, actual_downloaded_path, ec);
Checks::check_exit(VCPKG_LINE_INFO,
eq && !ec,
"Expected tool downloaded path to be %s, but was %s",
expected_downloaded_path.u8string(),
actual_downloaded_path.u8string());
return actual_downloaded_path;
#else
if (!fs.exists(tool_data.downloaded_path))
{
auto code = System::cmd_execute(Strings::format(
R"(curl -L '%s' --create-dirs --output '%s')", tool_data.url, tool_data.downloaded_path));
Checks::check_exit(VCPKG_LINE_INFO, code == 0, "curl failed while downloading %s", tool_data.url);
}
System::println("Extracting %s...", tool_name);
extract_archive(paths, tool_data.downloaded_path, tool_data.tool_dir_path);
System::println("Extracting %s... done.", tool_name);
Checks::check_exit(VCPKG_LINE_INFO,
fs.exists(tool_data.exe_path),
"Expected %s to exist after extracting",
tool_data.exe_path);
return tool_data.exe_path;
#endif
}
static fs::path get_cmake_path(const VcpkgPaths& paths)
{
std::vector<fs::path> candidate_paths;
#if defined(_WIN32) || defined(__APPLE__) || defined(__linux__)
static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "cmake");
candidate_paths.push_back(TOOL_DATA.exe_path);
#else
static const ToolData TOOL_DATA = ToolData{{3, 5, 1}, ""};
#endif
static const std::string VERSION_CHECK_ARGUMENTS = "--version";
const std::vector<fs::path> from_path = Files::find_from_PATH("cmake");
candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
const auto& program_files = System::get_program_files_platform_bitness();
if (const auto pf = program_files.get()) candidate_paths.push_back(*pf / "CMake" / "bin" / "cmake.exe");
const auto& program_files_32_bit = System::get_program_files_32_bit();
if (const auto pf = program_files_32_bit.get()) candidate_paths.push_back(*pf / "CMake" / "bin" / "cmake.exe");
const Optional<fs::path> path =
find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.required_version);
if (const auto p = path.get())
{
return *p;
}
return fetch_tool(paths, "cmake", TOOL_DATA);
}
static fs::path get_7za_path(const VcpkgPaths& paths)
{
#if defined(_WIN32)
static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "7zip");
if (!paths.get_filesystem().exists(TOOL_DATA.exe_path))
{
return fetch_tool(paths, "7zip", TOOL_DATA);
}
return TOOL_DATA.exe_path;
#else
Checks::exit_with_message(VCPKG_LINE_INFO, "Cannot download 7zip for non-Windows platforms.");
#endif
}
static fs::path get_ninja_path(const VcpkgPaths& paths)
{
static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "ninja");
std::vector<fs::path> candidate_paths;
candidate_paths.push_back(TOOL_DATA.exe_path);
const std::vector<fs::path> from_path = Files::find_from_PATH("ninja");
candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
auto path = find_if_has_equal_or_greater_version(candidate_paths, "--version", TOOL_DATA.required_version);
if (const auto p = path.get())
{
return *p;
}
return fetch_tool(paths, "ninja", TOOL_DATA);
}
static fs::path get_nuget_path(const VcpkgPaths& paths)
{
static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "nuget");
std::vector<fs::path> candidate_paths;
candidate_paths.push_back(TOOL_DATA.exe_path);
const std::vector<fs::path> from_path = Files::find_from_PATH("nuget");
candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
auto path = find_if_has_equal_or_greater_version(candidate_paths, "", TOOL_DATA.required_version);
if (const auto p = path.get())
{
return *p;
}
return fetch_tool(paths, "nuget", TOOL_DATA);
}
static fs::path get_git_path(const VcpkgPaths& paths)
{
#if defined(_WIN32)
static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "git");
#else
static const ToolData TOOL_DATA = ToolData{{2, 7, 4}, ""};
#endif
static const std::string VERSION_CHECK_ARGUMENTS = "--version";
std::vector<fs::path> candidate_paths;
#if defined(_WIN32)
candidate_paths.push_back(TOOL_DATA.exe_path);
#endif
const std::vector<fs::path> from_path = Files::find_from_PATH("git");
candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
const auto& program_files = System::get_program_files_platform_bitness();
if (const auto pf = program_files.get()) candidate_paths.push_back(*pf / "git" / "cmd" / "git.exe");
const auto& program_files_32_bit = System::get_program_files_32_bit();
if (const auto pf = program_files_32_bit.get()) candidate_paths.push_back(*pf / "git" / "cmd" / "git.exe");
const Optional<fs::path> path =
find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.required_version);
if (const auto p = path.get())
{
return *p;
}
return fetch_tool(paths, "git", TOOL_DATA);
}
static fs::path get_ifw_installerbase_path(const VcpkgPaths& paths)
{
static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "installerbase");
static const std::string VERSION_CHECK_ARGUMENTS = "--framework-version";
std::vector<fs::path> candidate_paths;
candidate_paths.push_back(TOOL_DATA.exe_path);
// TODO: Uncomment later
// const std::vector<fs::path> from_path = Files::find_from_PATH("installerbase");
// candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
// candidate_paths.push_back(fs::path(System::get_environment_variable("HOMEDRIVE").value_or("C:")) / "Qt" /
// "Tools" / "QtInstallerFramework" / "3.1" / "bin" / "installerbase.exe");
// candidate_paths.push_back(fs::path(System::get_environment_variable("HOMEDRIVE").value_or("C:")) / "Qt" /
// "QtIFW-3.1.0" / "bin" / "installerbase.exe");
const Optional<fs::path> path =
find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.required_version);
if (const auto p = path.get())
{
return *p;
}
return fetch_tool(paths, "installerbase", TOOL_DATA);
}
struct VisualStudioInstance
{
fs::path root_path;
std::string version;
std::string release_type;
std::string preference_weight; // Mostly unused, just for verification that order is as intended
std::string major_version() const { return version.substr(0, 2); }
};
static std::vector<VisualStudioInstance> get_visual_studio_instances(const VcpkgPaths& paths)
{
const fs::path script = paths.scripts / "findVisualStudioInstallationInstances.ps1";
const std::string output =
System::powershell_execute_and_capture_output("Detecting Visual Studio instances", script);
const std::vector<std::string> instances_as_strings = keep_data_lines(output);
Checks::check_exit(VCPKG_LINE_INFO,
!instances_as_strings.empty(),
"Could not detect any Visual Studio instances.\n"
"Powershell script:\n"
" %s\n"
"returned:\n"
"%s",
script.generic_string(),
output);
std::vector<VisualStudioInstance> instances;
for (const std::string& instance_as_string : instances_as_strings)
{
const std::vector<std::string> split = Strings::split(instance_as_string, "::");
Checks::check_exit(VCPKG_LINE_INFO,
split.size() == 4,
"Invalid Visual Studio instance format.\n"
"Expected: PreferenceWeight::ReleaseType::Version::PathToVisualStudio\n"
"Actual : %s\n",
instance_as_string);
instances.push_back({split.at(3), split.at(2), split.at(1), split.at(0)});
}
return instances;
}
std::vector<Toolset> find_toolset_instances(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 std::vector<VisualStudioInstance> vs_instances = get_visual_studio_instances(paths);
const bool v140_is_available = Util::find_if(vs_instances, [&](const VisualStudioInstance& vs_instance) {
return vs_instance.major_version() == "14";
}) != vs_instances.cend();
for (const VisualStudioInstance& vs_instance : vs_instances)
{
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 = Toolset{
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 = Toolset{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;
}
fs::path get_tool_path(const VcpkgPaths& paths, const std::string& tool)
{
// First deal with specially handled tools.
// For these we may look in locations like Program Files, the PATH etc as well as the auto-downloaded location.
if (tool == Tools::SEVEN_ZIP) return get_7za_path(paths);
if (tool == Tools::CMAKE) return get_cmake_path(paths);
if (tool == Tools::GIT) return get_git_path(paths);
if (tool == Tools::NINJA) return get_ninja_path(paths);
if (tool == Tools::NUGET) return get_nuget_path(paths);
if (tool == Tools::IFW_INSTALLER_BASE) return get_ifw_installerbase_path(paths);
if (tool == Tools::IFW_BINARYCREATOR)
return get_ifw_installerbase_path(paths).parent_path() / "binarycreator.exe";
if (tool == Tools::IFW_REPOGEN) return get_ifw_installerbase_path(paths).parent_path() / "repogen.exe";
// For other tools, we simply always auto-download them.
const ToolData tool_data = parse_tool_data_from_xml(paths, tool);
if (paths.get_filesystem().exists(tool_data.exe_path))
{
return tool_data.exe_path;
}
return fetch_tool(paths, tool, tool_data);
}
const CommandStructure COMMAND_STRUCTURE = {
Strings::format("The argument should be tool name\n%s", Help::create_example_string("fetch cmake")),
1,
1,
{},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
Util::unused(args.parse_arguments(COMMAND_STRUCTURE));
const std::string tool = args.command_arguments[0];
const fs::path tool_path = get_tool_path(paths, tool);
System::println(tool_path.u8string());
Checks::exit_success(VCPKG_LINE_INFO);
}
}

View File

@ -276,7 +276,7 @@ CMake projects should use: "-DCMAKE_TOOLCHAIN_FILE=%s")",
{
auto& fs = paths.get_filesystem();
const fs::path& nuget_exe = paths.get_nuget_exe();
const fs::path& nuget_exe = paths.get_tool_exe(Tools::NUGET);
const fs::path& buildsystems_dir = paths.buildsystems;
const fs::path tmp_dir = buildsystems_dir / "tmp";

View File

@ -79,7 +79,7 @@ namespace vcpkg::Commands::PortsDiff
{
std::error_code ec;
auto& fs = paths.get_filesystem();
const fs::path& git_exe = paths.get_git_exe();
const fs::path& git_exe = paths.get_tool_exe(Tools::GIT);
const fs::path dot_git_dir = paths.root / ".git";
const std::string ports_dir_name_as_string = paths.ports.filename().u8string();
const fs::path temp_checkout_path =
@ -130,7 +130,7 @@ namespace vcpkg::Commands::PortsDiff
{
args.parse_arguments(COMMAND_STRUCTURE);
const fs::path& git_exe = paths.get_git_exe();
const fs::path& git_exe = paths.get_tool_exe(Tools::GIT);
const std::string git_commit_id_for_previous_snapshot = args.command_arguments.at(0);
const std::string git_commit_id_for_current_snapshot =

View File

@ -123,7 +123,7 @@ namespace vcpkg::Export
const fs::path& output_dir)
{
Files::Filesystem& fs = paths.get_filesystem();
const fs::path& nuget_exe = paths.get_nuget_exe();
const fs::path& nuget_exe = paths.get_tool_exe(Tools::NUGET);
// This file will be placed in "build\native" in the nuget package. Therefore, go up two dirs.
const std::string targets_redirect_content =
@ -189,7 +189,7 @@ namespace vcpkg::Export
const fs::path& output_dir,
const ArchiveFormat& format)
{
const fs::path& cmake_exe = paths.get_cmake_exe();
const fs::path& cmake_exe = paths.get_tool_exe(Tools::CMAKE);
const std::string exported_dir_filename = raw_exported_dir.filename().u8string();
const std::string exported_archive_filename =

View File

@ -5,401 +5,13 @@
#include <vcpkg/base/system.h>
#include <vcpkg/base/util.h>
#include <vcpkg/build.h>
#include <vcpkg/commands.h>
#include <vcpkg/metrics.h>
#include <vcpkg/packagespec.h>
#include <vcpkg/vcpkgpaths.h>
namespace vcpkg
{
static constexpr CStringView V_120 = "v120";
static constexpr CStringView V_140 = "v140";
static constexpr CStringView V_141 = "v141";
struct ToolData
{
std::array<int, 3> required_version;
fs::path exe_path;
std::string url;
fs::path downloaded_path;
fs::path tool_dir_path;
};
static Optional<std::array<int, 3>> parse_version_string(const std::string& version_as_string)
{
static const std::regex RE(R"###((\d+)\.(\d+)\.(\d+))###");
std::match_results<std::string::const_iterator> match;
const auto found = std::regex_search(version_as_string, match, RE);
if (!found)
{
return {};
}
const int d1 = atoi(match[1].str().c_str());
const int d2 = atoi(match[2].str().c_str());
const int d3 = atoi(match[3].str().c_str());
const std::array<int, 3> result = {d1, d2, d3};
return result;
}
static ToolData parse_tool_data_from_xml(const VcpkgPaths& paths, const std::string& tool)
{
#if defined(_WIN32)
static constexpr StringLiteral OS_STRING = "windows";
#elif defined(__APPLE__)
static constexpr StringLiteral OS_STRING = "osx";
#else // assume linux
static constexpr StringLiteral OS_STRING = "linux";
#endif
static const fs::path XML_PATH = paths.scripts / "vcpkgTools.xml";
const auto maybe_get_string_inside_tags = [](const std::string& input,
const std::regex& regex) -> Optional<std::string> {
std::smatch match;
const bool has_match = std::regex_search(input.cbegin(), input.cend(), match, regex);
if (!has_match) return nullopt;
return match[1];
};
const auto get_string_inside_tags =
[](const std::string& input, const std::regex& regex, const std::string& tag_name) -> std::string {
std::smatch match;
const bool has_match = std::regex_search(input.cbegin(), input.cend(), match, regex);
Checks::check_exit(
VCPKG_LINE_INFO, has_match, "Could not find tag <%s> in %s", tag_name, XML_PATH.generic_string());
return match[1];
};
static const std::string XML = paths.get_filesystem().read_contents(XML_PATH).value_or_exit(VCPKG_LINE_INFO);
static const std::regex VERSION_REGEX{R"###(<requiredVersion>([\s\S]*?)</requiredVersion>)###"};
static const std::regex EXE_RELATIVE_PATH_REGEX{
Strings::format(R"###(<exeRelativePath>([\s\S]*?)</exeRelativePath>)###")};
static const std::regex ARCHIVE_RELATIVE_PATH_REGEX{
Strings::format(R"###(<archiveRelativePath>([\s\S]*?)</archiveRelativePath>)###")};
static const std::regex URL_REGEX{Strings::format(R"###(<url>([\s\S]*?)</url>)###")};
std::regex tool_regex{
Strings::format(R"###(<tool[\s]+name="%s"[\s]+os="%s">([\s\S]*?)<\/tool>)###", tool, OS_STRING)};
std::smatch match_tool;
bool has_match_tool = std::regex_search(XML.cbegin(), XML.cend(), match_tool, tool_regex);
if (!has_match_tool && OS_STRING == "windows") // Legacy support. Change introduced in vcpkg v0.0.107.
{
tool_regex = Strings::format(R"###(<tool[\s]+name="%s">([\s\S]*?)<\/tool>)###", tool);
has_match_tool = std::regex_search(XML.cbegin(), XML.cend(), match_tool, tool_regex);
}
Checks::check_exit(VCPKG_LINE_INFO,
has_match_tool,
"Could not find entry for tool [%s] in %s",
tool,
XML_PATH.generic_string());
const std::string tool_data_as_string = get_string_inside_tags(XML, tool_regex, tool);
const std::string required_version_as_string =
get_string_inside_tags(tool_data_as_string, VERSION_REGEX, "requiredVersion");
const std::string url = get_string_inside_tags(tool_data_as_string, URL_REGEX, "url");
const std::string exe_relative_path =
get_string_inside_tags(tool_data_as_string, EXE_RELATIVE_PATH_REGEX, "exeRelativePath");
auto archive_relative_path = maybe_get_string_inside_tags(tool_data_as_string, ARCHIVE_RELATIVE_PATH_REGEX);
const Optional<std::array<int, 3>> required_version = parse_version_string(required_version_as_string);
Checks::check_exit(VCPKG_LINE_INFO,
required_version.has_value(),
"Could not parse version for tool %s. Version string was: %s",
tool,
required_version_as_string);
const std::string tool_dir_name = Strings::format("%s-%s-%s", tool, required_version_as_string, OS_STRING);
const fs::path tool_dir_path = paths.downloads / "tools" / tool_dir_name;
const fs::path exe_path = tool_dir_path / exe_relative_path;
return ToolData{*required_version.get(),
exe_path,
url,
paths.downloads / archive_relative_path.value_or(exe_relative_path),
tool_dir_path};
}
static bool exists_and_has_equal_or_greater_version(const std::string& version_cmd,
const std::array<int, 3>& expected_version)
{
const auto rc = System::cmd_execute_and_capture_output(Strings::format(R"(%s)", version_cmd));
if (rc.exit_code != 0)
{
return false;
}
const Optional<std::array<int, 3>> v = parse_version_string(rc.output);
if (!v.has_value())
{
return false;
}
const std::array<int, 3> actual_version = *v.get();
return (actual_version[0] > expected_version[0] ||
(actual_version[0] == expected_version[0] && actual_version[1] > expected_version[1]) ||
(actual_version[0] == expected_version[0] && actual_version[1] == expected_version[1] &&
actual_version[2] >= expected_version[2]));
}
static Optional<fs::path> find_if_has_equal_or_greater_version(const std::vector<fs::path>& candidate_paths,
const std::string& version_check_arguments,
const std::array<int, 3>& expected_version)
{
auto it = Util::find_if(candidate_paths, [&](const fs::path& p) {
const std::string cmd = Strings::format(R"("%s" %s)", p.u8string(), version_check_arguments);
return exists_and_has_equal_or_greater_version(cmd, expected_version);
});
if (it != candidate_paths.cend())
{
return std::move(*it);
}
return nullopt;
}
static std::vector<std::string> keep_data_lines(const std::string& data_blob)
{
static const std::regex DATA_LINE_REGEX(R"(<sol>::(.+?)(?=::<eol>))");
std::vector<std::string> data_lines;
const std::sregex_iterator it(data_blob.cbegin(), data_blob.cend(), DATA_LINE_REGEX);
const std::sregex_iterator end;
for (std::sregex_iterator i = it; i != end; ++i)
{
const std::smatch match = *i;
data_lines.push_back(match[1].str());
}
return data_lines;
}
static void extract_archive(const VcpkgPaths& paths, const fs::path& archive, const fs::path& to_path)
{
Files::Filesystem& fs = paths.get_filesystem();
const fs::path to_path_partial = to_path.u8string() + ".partial";
std::error_code ec;
fs.remove_all(to_path_partial, ec);
fs.create_directories(to_path_partial, ec);
const auto ext = archive.extension();
if (ext == ".gz" && ext.extension() != ".tar")
{
const auto code = System::cmd_execute(
Strings::format(R"(cd '%s' && tar xzf '%s')", to_path_partial.u8string(), archive.u8string()));
Checks::check_exit(VCPKG_LINE_INFO, code == 0, "tar failed while extracting %s", archive.u8string());
}
else if (ext == ".zip")
{
const auto code = System::cmd_execute(
Strings::format(R"(cd '%s' && unzip -qqo '%s')", to_path_partial.u8string(), archive.u8string()));
Checks::check_exit(VCPKG_LINE_INFO, code == 0, "unzip failed while extracting %s", archive.u8string());
}
else
{
Checks::exit_with_message(VCPKG_LINE_INFO, "Unexpected archive extension: %s", ext.u8string());
}
fs.rename(to_path_partial, to_path);
}
static fs::path fetch_tool(const VcpkgPaths& paths, const std::string& tool_name, const ToolData& tool_data)
{
const auto& fs = paths.get_filesystem();
const fs::path& scripts_folder = paths.scripts;
const std::array<int, 3>& version = tool_data.required_version;
const std::string version_as_string = Strings::format("%d.%d.%d", version[0], version[1], version[2]);
System::println("A suitable version of %s was not found (required v%s). Downloading portable %s v%s...",
tool_name,
version_as_string,
tool_name,
version_as_string);
#if defined(_WIN32)
const fs::path script = scripts_folder / "fetchtool.ps1";
const std::string title = Strings::format(
"Fetching %s version %s (No sufficient installed version was found)", tool_name, version_as_string);
const System::PowershellParameter tool_param("tool", tool_name);
const std::string output = System::powershell_execute_and_capture_output(title, script, {tool_param});
const std::vector<std::string> tool_path = keep_data_lines(output);
Checks::check_exit(VCPKG_LINE_INFO, tool_path.size() == 1, "Expected tool path, but got %s", output);
const fs::path actual_downloaded_path = Strings::trim(std::string{tool_path.at(0)});
const fs::path& expected_downloaded_path = tool_data.exe_path;
std::error_code ec;
const auto eq = fs::stdfs::equivalent(expected_downloaded_path, actual_downloaded_path, ec);
Checks::check_exit(VCPKG_LINE_INFO,
eq && !ec,
"Expected tool downloaded path to be %s, but was %s",
expected_downloaded_path.u8string(),
actual_downloaded_path.u8string());
return actual_downloaded_path;
#else
if (!fs.exists(tool_data.downloaded_path))
{
auto code = System::cmd_execute(Strings::format(
R"(curl -L '%s' --create-dirs --output '%s')", tool_data.url, tool_data.downloaded_path));
Checks::check_exit(VCPKG_LINE_INFO, code == 0, "curl failed while downloading %s", tool_data.url);
}
System::println("Extracting %s...", tool_name);
extract_archive(paths, tool_data.downloaded_path, tool_data.tool_dir_path);
System::println("Extracting %s... done.", tool_name);
Checks::check_exit(VCPKG_LINE_INFO,
fs.exists(tool_data.exe_path),
"Expected %s to exist after extracting",
tool_data.exe_path);
return tool_data.exe_path;
#endif
}
static fs::path get_cmake_path(const VcpkgPaths& paths)
{
std::vector<fs::path> candidate_paths;
#if defined(_WIN32) || defined(__APPLE__) || defined(__linux__)
static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "cmake");
candidate_paths.push_back(TOOL_DATA.exe_path);
#else
static const ToolData TOOL_DATA = ToolData{{3, 5, 1}, ""};
#endif
static const std::string VERSION_CHECK_ARGUMENTS = "--version";
const std::vector<fs::path> from_path = Files::find_from_PATH("cmake");
candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
const auto& program_files = System::get_program_files_platform_bitness();
if (const auto pf = program_files.get()) candidate_paths.push_back(*pf / "CMake" / "bin" / "cmake.exe");
const auto& program_files_32_bit = System::get_program_files_32_bit();
if (const auto pf = program_files_32_bit.get()) candidate_paths.push_back(*pf / "CMake" / "bin" / "cmake.exe");
const Optional<fs::path> path =
find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.required_version);
if (const auto p = path.get())
{
return *p;
}
return fetch_tool(paths, "cmake", TOOL_DATA);
}
static fs::path get_7za_path(const VcpkgPaths& paths)
{
#if defined(_WIN32)
static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "7zip");
if (!paths.get_filesystem().exists(TOOL_DATA.exe_path))
{
return fetch_tool(paths, "7zip", TOOL_DATA);
}
return TOOL_DATA.exe_path;
#else
Checks::exit_with_message(VCPKG_LINE_INFO, "Cannot download 7zip for non-Windows platforms.");
#endif
}
static fs::path get_ninja_path(const VcpkgPaths& paths)
{
static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "ninja");
std::vector<fs::path> candidate_paths;
candidate_paths.push_back(TOOL_DATA.exe_path);
const std::vector<fs::path> from_path = Files::find_from_PATH("ninja");
candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
auto path = find_if_has_equal_or_greater_version(candidate_paths, "--version", TOOL_DATA.required_version);
if (const auto p = path.get())
{
return *p;
}
return fetch_tool(paths, "ninja", TOOL_DATA);
}
static fs::path get_nuget_path(const VcpkgPaths& paths)
{
static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "nuget");
std::vector<fs::path> candidate_paths;
candidate_paths.push_back(TOOL_DATA.exe_path);
const std::vector<fs::path> from_path = Files::find_from_PATH("nuget");
candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
auto path = find_if_has_equal_or_greater_version(candidate_paths, "", TOOL_DATA.required_version);
if (const auto p = path.get())
{
return *p;
}
return fetch_tool(paths, "nuget", TOOL_DATA);
}
static fs::path get_git_path(const VcpkgPaths& paths)
{
#if defined(_WIN32)
static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "git");
#else
static const ToolData TOOL_DATA = ToolData{{2, 7, 4}, ""};
#endif
static const std::string VERSION_CHECK_ARGUMENTS = "--version";
std::vector<fs::path> candidate_paths;
#if defined(_WIN32)
candidate_paths.push_back(TOOL_DATA.exe_path);
#endif
const std::vector<fs::path> from_path = Files::find_from_PATH("git");
candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
const auto& program_files = System::get_program_files_platform_bitness();
if (const auto pf = program_files.get()) candidate_paths.push_back(*pf / "git" / "cmd" / "git.exe");
const auto& program_files_32_bit = System::get_program_files_32_bit();
if (const auto pf = program_files_32_bit.get()) candidate_paths.push_back(*pf / "git" / "cmd" / "git.exe");
const Optional<fs::path> path =
find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.required_version);
if (const auto p = path.get())
{
return *p;
}
return fetch_tool(paths, "git", TOOL_DATA);
}
static fs::path get_ifw_installerbase_path(const VcpkgPaths& paths)
{
static const ToolData TOOL_DATA = parse_tool_data_from_xml(paths, "installerbase");
static const std::string VERSION_CHECK_ARGUMENTS = "--framework-version";
std::vector<fs::path> candidate_paths;
candidate_paths.push_back(TOOL_DATA.exe_path);
// TODO: Uncomment later
// const std::vector<fs::path> from_path = Files::find_from_PATH("installerbase");
// candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend());
// candidate_paths.push_back(fs::path(System::get_environment_variable("HOMEDRIVE").value_or("C:")) / "Qt" /
// "Tools" / "QtInstallerFramework" / "3.1" / "bin" / "installerbase.exe");
// candidate_paths.push_back(fs::path(System::get_environment_variable("HOMEDRIVE").value_or("C:")) / "Qt" /
// "QtIFW-3.1.0" / "bin" / "installerbase.exe");
const Optional<fs::path> path =
find_if_has_equal_or_greater_version(candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.required_version);
if (const auto p = path.get())
{
return *p;
}
return fetch_tool(paths, "installerbase", TOOL_DATA);
}
Expected<VcpkgPaths> VcpkgPaths::create(const fs::path& vcpkg_root_dir, const std::string& default_vs_path)
{
std::error_code ec;
@ -471,266 +83,22 @@ namespace vcpkg
bool VcpkgPaths::is_valid_triplet(const Triplet& t) const
{
auto it = Util::find_if(this->get_available_triplets(),
[&](auto&& available_triplet) { return t.canonical_name() == available_triplet; });
const auto it = Util::find_if(this->get_available_triplets(), [&](auto&& available_triplet) {
return t.canonical_name() == available_triplet;
});
return it != this->get_available_triplets().cend();
}
const fs::path& VcpkgPaths::get_7za_exe() const
const fs::path& VcpkgPaths::get_tool_exe(const std::string& tool) const
{
return this->_7za_exe.get_lazy([this]() { return get_7za_path(*this); });
}
const fs::path& VcpkgPaths::get_cmake_exe() const
{
return this->cmake_exe.get_lazy([this]() { return get_cmake_path(*this); });
}
const fs::path& VcpkgPaths::get_git_exe() const
{
return this->git_exe.get_lazy([this]() { return get_git_path(*this); });
}
const fs::path& VcpkgPaths::get_ninja_exe() const
{
return this->ninja_exe.get_lazy([this]() { return get_ninja_path(*this); });
}
const fs::path& VcpkgPaths::get_nuget_exe() const
{
return this->nuget_exe.get_lazy([this]() { return get_nuget_path(*this); });
}
const fs::path& VcpkgPaths::get_ifw_installerbase_exe() const
{
return this->ifw_installerbase_exe.get_lazy([this]() { return get_ifw_installerbase_path(*this); });
}
const fs::path& VcpkgPaths::get_ifw_binarycreator_exe() const
{
return this->ifw_binarycreator_exe.get_lazy(
[this]() { return get_ifw_installerbase_exe().parent_path() / "binarycreator.exe"; });
}
const fs::path& VcpkgPaths::get_ifw_repogen_exe() const
{
return this->ifw_repogen_exe.get_lazy(
[this]() { return get_ifw_installerbase_exe().parent_path() / "repogen.exe"; });
}
struct VisualStudioInstance
{
fs::path root_path;
std::string version;
std::string release_type;
std::string preference_weight; // Mostly unused, just for verification that order is as intended
std::string major_version() const { return version.substr(0, 2); }
};
static std::vector<VisualStudioInstance> get_visual_studio_instances(const VcpkgPaths& paths)
{
const fs::path script = paths.scripts / "findVisualStudioInstallationInstances.ps1";
const std::string output =
System::powershell_execute_and_capture_output("Detecting Visual Studio instances", script);
const std::vector<std::string> instances_as_strings = keep_data_lines(output);
Checks::check_exit(VCPKG_LINE_INFO,
!instances_as_strings.empty(),
"Could not detect any Visual Studio instances.\n"
"Powershell script:\n"
" %s\n"
"returned:\n"
"%s",
script.generic_string(),
output);
std::vector<VisualStudioInstance> instances;
for (const std::string& instance_as_string : instances_as_strings)
const auto it = this->tool_paths.find(tool);
if (it != this->tool_paths.cend())
{
const std::vector<std::string> split = Strings::split(instance_as_string, "::");
Checks::check_exit(VCPKG_LINE_INFO,
split.size() == 4,
"Invalid Visual Studio instance format.\n"
"Expected: PreferenceWeight::ReleaseType::Version::PathToVisualStudio\n"
"Actual : %s\n",
instance_as_string);
instances.push_back({split.at(3), split.at(2), split.at(1), split.at(0)});
return it->second;
}
return instances;
}
static std::vector<Toolset> find_toolset_instances(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 std::vector<VisualStudioInstance> vs_instances = get_visual_studio_instances(paths);
const bool v140_is_available = Util::find_if(vs_instances, [&](const VisualStudioInstance& vs_instance) {
return vs_instance.major_version() == "14";
}) != vs_instances.cend();
for (const VisualStudioInstance& vs_instance : vs_instances)
{
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 = Toolset{
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 = Toolset{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;
this->tool_paths[tool] = Commands::Fetch::get_tool_path(*this, tool);
return this->tool_paths[tool];
}
const Toolset& VcpkgPaths::get_toolset(const Build::PreBuildInfo& prebuildinfo) const
@ -754,7 +122,7 @@ namespace vcpkg
// Invariant: toolsets are non-empty and sorted with newest at back()
const std::vector<Toolset>& vs_toolsets =
this->toolsets.get_lazy([this]() { return find_toolset_instances(*this); });
this->toolsets.get_lazy([this]() { return Commands::Fetch::find_toolset_instances(*this); });
std::vector<const Toolset*> candidates = Util::element_pointers(vs_toolsets);
const auto tsv = prebuildinfo.platform_toolset.get();

View File

@ -214,6 +214,7 @@
<ClCompile Include="..\src\vcpkg\commands.edit.cpp" />
<ClCompile Include="..\src\vcpkg\commands.env.cpp" />
<ClCompile Include="..\src\vcpkg\commands.exportifw.cpp" />
<ClCompile Include="..\src\vcpkg\commands.fetch.cpp" />
<ClCompile Include="..\src\vcpkg\commands.hash.cpp" />
<ClCompile Include="..\src\vcpkg\commands.import.cpp" />
<ClCompile Include="..\src\vcpkg\commands.integrate.cpp" />

View File

@ -198,6 +198,9 @@
<ClCompile Include="..\src\vcpkg\commands.upgrade.cpp">
<Filter>Source Files\vcpkg</Filter>
</ClCompile>
<ClCompile Include="..\src\vcpkg\commands.fetch.cpp">
<Filter>Source Files\vcpkg</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\pch.h">