diff --git a/toolsrc/include/vcpkg_paths.h b/toolsrc/include/vcpkg_paths.h index ba6defb9f2..99fd149058 100644 --- a/toolsrc/include/vcpkg_paths.h +++ b/toolsrc/include/vcpkg_paths.h @@ -3,6 +3,7 @@ #include "expected.h" #include "package_spec.h" #include "BinaryParagraph.h" +#include "lazy.h" namespace vcpkg { @@ -35,5 +36,14 @@ namespace vcpkg fs::path vcpkg_dir_updates; fs::path ports_cmake; + + const fs::path& get_cmake_exe() const; + const fs::path& get_git_exe() const; + const fs::path& get_nuget_exe() const; + + private: + lazy cmake_exe; + lazy git_exe; + lazy nuget_exe; }; } diff --git a/toolsrc/src/vcpkg_paths.cpp b/toolsrc/src/vcpkg_paths.cpp index 8d7060a022..34bab1dda2 100644 --- a/toolsrc/src/vcpkg_paths.cpp +++ b/toolsrc/src/vcpkg_paths.cpp @@ -4,9 +4,153 @@ #include "metrics.h" #include "vcpkg_System.h" #include "package_spec.h" +#include "vcpkg_Environment.h" namespace vcpkg { + static bool exists_and_has_equal_or_greater_version(const std::wstring& version_cmd, const std::array& expected_version) + { + static const std::regex re(R"###((\d+)\.(\d+)\.(\d+))###"); + + auto rc = System::cmd_execute_and_capture_output(Strings::wformat(LR"(%s 2>&1)", version_cmd)); + if (rc.exit_code != 0) + { + return false; + } + + std::match_results match; + auto found = std::regex_search(rc.output, match, re); + if (!found) + { + return false; + } + + int d1 = atoi(match[1].str().c_str()); + int d2 = atoi(match[2].str().c_str()); + int d3 = atoi(match[3].str().c_str()); + if (d1 > expected_version[0] || (d1 == expected_version[0] && d2 > expected_version[1]) || (d1 == expected_version[0] && d2 == expected_version[1] && d3 >= expected_version[2])) + { + // satisfactory version found + return true; + } + + return false; + } + + static optional find_if_has_equal_or_greater_version(const std::vector& candidate_paths, const std::wstring& version_check_arguments, const std::array& expected_version) + { + auto it = std::find_if(candidate_paths.cbegin(), candidate_paths.cend(), [&](const fs::path& p) + { + const std::wstring cmd = Strings::wformat(LR"("%s" %s)", p.native(), version_check_arguments); + return exists_and_has_equal_or_greater_version(cmd, expected_version); + }); + + if (it != candidate_paths.cend()) + { + return std::make_unique(std::move(*it)); + } + + return nullptr; + } + + static std::vector find_from_PATH(const std::wstring& name) + { + const std::wstring cmd = Strings::wformat(L"where.exe %s", name); + auto out = System::cmd_execute_and_capture_output(cmd); + if (out.exit_code != 0) + { + return {}; + } + + const std::vector paths_to_add = Strings::split(out.output, "\n"); + std::vector v; + v.insert(v.end(), paths_to_add.cbegin(), paths_to_add.cend()); + return v; + } + + static fs::path fetch_dependency(const fs::path scripts_folder, const std::wstring& tool_name, const fs::path& expected_downloaded_path) + { + const fs::path script = scripts_folder / "fetchDependency.ps1"; + auto install_cmd = System::create_powershell_script_cmd(script, Strings::wformat(L"-Dependency %s", tool_name)); + System::exit_code_and_output rc = System::cmd_execute_and_capture_output(install_cmd); + if (rc.exit_code) + { + System::println(System::color::error, "Launching powershell failed or was denied"); + TrackProperty("error", "powershell install failed"); + TrackProperty("installcmd", install_cmd); + exit(rc.exit_code); + } + + const fs::path actual_downloaded_path = rc.output; + Checks::check_exit(expected_downloaded_path == actual_downloaded_path, "Expected dependency downloaded path to be %s, but was %s", + expected_downloaded_path.generic_string(), actual_downloaded_path.generic_string()); + return actual_downloaded_path; + } + + static fs::path get_cmake_path(const fs::path& downloads_folder, const fs::path scripts_folder) + { + static constexpr std::array expected_version = { 3,8,0 }; + static const std::wstring version_check_arguments = L"--version"; + + const fs::path downloaded_copy = downloads_folder / "cmake-3.8.0-rc1-win32-x86" / "bin" / "cmake.exe"; + const std::vector from_path = find_from_PATH(L"cmake"); + + std::vector candidate_paths; + candidate_paths.push_back(downloaded_copy); + candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); + candidate_paths.push_back(Environment::get_ProgramFiles_platform_bitness() / "CMake" / "bin" / "cmake.exe"); + candidate_paths.push_back(Environment::get_ProgramFiles_32_bit() / "CMake" / "bin"); + + if (auto ret = find_if_has_equal_or_greater_version(candidate_paths, version_check_arguments, expected_version)) + { + return *ret; + } + + return fetch_dependency(scripts_folder, L"cmake", downloaded_copy); + } + + fs::path get_nuget_path(const fs::path& downloads_folder, const fs::path scripts_folder) + { + static constexpr std::array expected_version = { 3,3,0 }; + static const std::wstring version_check_arguments = L""; + + const fs::path downloaded_copy = downloads_folder / "nuget-3.5.0" / "nuget.exe"; + const std::vector from_path = find_from_PATH(L"nuget"); + + std::vector candidate_paths; + candidate_paths.push_back(downloaded_copy); + candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); + + if (auto ret = find_if_has_equal_or_greater_version(candidate_paths, version_check_arguments, expected_version)) + { + return *ret; + } + + return fetch_dependency(scripts_folder, L"nuget", downloaded_copy); + } + + fs::path get_git_path(const fs::path& downloads_folder, const fs::path scripts_folder) + { + static constexpr std::array expected_version = { 2,0,0 }; + static const std::wstring version_check_arguments = L"--version"; + + const fs::path downloaded_copy = downloads_folder / "MinGit-2.11.1-32-bit" / "cmd" / "git.exe"; + const std::vector from_path = find_from_PATH(L"git"); + + std::vector candidate_paths; + candidate_paths.push_back(downloaded_copy); + candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); + candidate_paths.push_back(Environment::get_ProgramFiles_platform_bitness() / "git" / "cmd" / "git.exe"); + candidate_paths.push_back(Environment::get_ProgramFiles_32_bit() / "git" / "cmd" / "git.exe"); + + if (auto ret = find_if_has_equal_or_greater_version(candidate_paths, version_check_arguments, expected_version)) + { + return *ret; + } + + return fetch_dependency(scripts_folder, L"git", downloaded_copy); + } + expected vcpkg_paths::create(const fs::path& vcpkg_root_dir) { std::error_code ec; @@ -43,6 +187,7 @@ namespace vcpkg paths.vcpkg_dir_updates = paths.vcpkg_dir / "updates"; paths.ports_cmake = paths.scripts / "ports.cmake"; + return paths; } @@ -80,4 +225,19 @@ namespace vcpkg } return false; } + + const fs::path& vcpkg_paths::get_cmake_exe() const + { + return this->cmake_exe.get_lazy([this]() { return get_cmake_path(this->downloads, this->scripts); }); + } + + const fs::path& vcpkg_paths::get_git_exe() const + { + return this->git_exe.get_lazy([this]() { return get_git_path(this->downloads, this->scripts); }); + } + + const fs::path& vcpkg_paths::get_nuget_exe() const + { + return this->nuget_exe.get_lazy([this]() { return get_nuget_path(this->downloads, this->scripts); }); + } }