aboutsummaryrefslogtreecommitdiff
path: root/toolsrc/src
diff options
context:
space:
mode:
authorAlexander Karatarakis <alkarata@microsoft.com>2018-04-04 01:45:45 -0700
committerAlexander Karatarakis <alkarata@microsoft.com>2018-04-06 17:24:46 -0700
commit0c0f68939e0d8367e55793f80f0000f2a43a812a (patch)
tree9c7456dac30c2af53f982a7a4791cd790caa4e50 /toolsrc/src
parent54c68da907e4881d29e8017e085e6786e1c34ace (diff)
downloadvcpkg-0c0f68939e0d8367e55793f80f0000f2a43a812a.tar.gz
vcpkg-0c0f68939e0d8367e55793f80f0000f2a43a812a.zip
Introduce "vcpkg fetch"
Diffstat (limited to 'toolsrc/src')
-rw-r--r--toolsrc/src/vcpkg/build.cpp15
-rw-r--r--toolsrc/src/vcpkg/commands.cpp9
-rw-r--r--toolsrc/src/vcpkg/commands.create.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.exportifw.cpp6
-rw-r--r--toolsrc/src/vcpkg/commands.fetch.cpp656
-rw-r--r--toolsrc/src/vcpkg/commands.integrate.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.portsdiff.cpp4
-rw-r--r--toolsrc/src/vcpkg/export.cpp4
-rw-r--r--toolsrc/src/vcpkg/vcpkgpaths.cpp654
9 files changed, 687 insertions, 665 deletions
diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp
index 79a55bd36..86d171534 100644
--- a/toolsrc/src/vcpkg/build.cpp
+++ b/toolsrc/src/vcpkg/build.cpp
@@ -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");
diff --git a/toolsrc/src/vcpkg/commands.cpp b/toolsrc/src/vcpkg/commands.cpp
index d9c0e54cd..e98ff711a 100644
--- a/toolsrc/src/vcpkg/commands.cpp
+++ b/toolsrc/src/vcpkg/commands.cpp
@@ -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;
}
}
diff --git a/toolsrc/src/vcpkg/commands.create.cpp b/toolsrc/src/vcpkg/commands.create.cpp
index 25c34cf09..60769c9ef 100644
--- a/toolsrc/src/vcpkg/commands.create.cpp
+++ b/toolsrc/src/vcpkg/commands.create.cpp
@@ -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}};
diff --git a/toolsrc/src/vcpkg/commands.exportifw.cpp b/toolsrc/src/vcpkg/commands.exportifw.cpp
index 58d9aa0be..ae106196a 100644
--- a/toolsrc/src/vcpkg/commands.exportifw.cpp
+++ b/toolsrc/src/vcpkg/commands.exportifw.cpp
@@ -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);
diff --git a/toolsrc/src/vcpkg/commands.fetch.cpp b/toolsrc/src/vcpkg/commands.fetch.cpp
new file mode 100644
index 000000000..95c47e2db
--- /dev/null
+++ b/toolsrc/src/vcpkg/commands.fetch.cpp
@@ -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);
+ }
+}
diff --git a/toolsrc/src/vcpkg/commands.integrate.cpp b/toolsrc/src/vcpkg/commands.integrate.cpp
index 36e4e56e7..d6ae27181 100644
--- a/toolsrc/src/vcpkg/commands.integrate.cpp
+++ b/toolsrc/src/vcpkg/commands.integrate.cpp
@@ -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";
diff --git a/toolsrc/src/vcpkg/commands.portsdiff.cpp b/toolsrc/src/vcpkg/commands.portsdiff.cpp
index dba04ce5b..c6b02582f 100644
--- a/toolsrc/src/vcpkg/commands.portsdiff.cpp
+++ b/toolsrc/src/vcpkg/commands.portsdiff.cpp
@@ -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 =
diff --git a/toolsrc/src/vcpkg/export.cpp b/toolsrc/src/vcpkg/export.cpp
index 16c84f99d..32151c791 100644
--- a/toolsrc/src/vcpkg/export.cpp
+++ b/toolsrc/src/vcpkg/export.cpp
@@ -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 =
diff --git a/toolsrc/src/vcpkg/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp
index f2086257e..4f998d579 100644
--- a/toolsrc/src/vcpkg/vcpkgpaths.cpp
+++ b/toolsrc/src/vcpkg/vcpkgpaths.cpp
@@ -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 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;
- }
-
- 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())
+ const auto it = this->tool_paths.find(tool);
+ if (it != this->tool_paths.cend())
{
- 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 it->second;
}
- 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();