diff options
| author | Alexander Karatarakis <alkarata@microsoft.com> | 2018-04-04 01:45:45 -0700 |
|---|---|---|
| committer | Alexander Karatarakis <alkarata@microsoft.com> | 2018-04-06 17:24:46 -0700 |
| commit | 0c0f68939e0d8367e55793f80f0000f2a43a812a (patch) | |
| tree | 9c7456dac30c2af53f982a7a4791cd790caa4e50 /toolsrc/src | |
| parent | 54c68da907e4881d29e8017e085e6786e1c34ace (diff) | |
| download | vcpkg-0c0f68939e0d8367e55793f80f0000f2a43a812a.tar.gz vcpkg-0c0f68939e0d8367e55793f80f0000f2a43a812a.zip | |
Introduce "vcpkg fetch"
Diffstat (limited to 'toolsrc/src')
| -rw-r--r-- | toolsrc/src/vcpkg/build.cpp | 15 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/commands.cpp | 9 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/commands.create.cpp | 2 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/commands.exportifw.cpp | 6 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/commands.fetch.cpp | 656 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/commands.integrate.cpp | 2 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/commands.portsdiff.cpp | 4 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/export.cpp | 4 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/vcpkgpaths.cpp | 654 |
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(); |
