diff options
| author | eao197 <eao197@gmail.com> | 2018-05-30 19:25:16 +0300 |
|---|---|---|
| committer | eao197 <eao197@gmail.com> | 2018-05-30 19:25:16 +0300 |
| commit | 99eb78cf2dd8b000ac195dd9ebba4fb344dc5baa (patch) | |
| tree | 18072db815a8f417c4d63a00ae95286642f0dff3 /toolsrc/src | |
| parent | 34257a50ceda0bfa05e59b532f71223b70f39d52 (diff) | |
| parent | 842252373992a9c7b74f041607b47754d61cc0c8 (diff) | |
| download | vcpkg-99eb78cf2dd8b000ac195dd9ebba4fb344dc5baa.tar.gz vcpkg-99eb78cf2dd8b000ac195dd9ebba4fb344dc5baa.zip | |
Merge https://github.com/Microsoft/vcpkg
Diffstat (limited to 'toolsrc/src')
| -rw-r--r-- | toolsrc/src/tests.paragraph.cpp | 9 | ||||
| -rw-r--r-- | toolsrc/src/tests.utils.cpp | 30 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg.cpp | 4 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/base/checks.cpp | 2 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/base/files.cpp | 7 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/base/strings.cpp | 18 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/base/system.cpp | 103 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/binaryparagraph.cpp | 2 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/build.cpp | 36 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/commands.cache.cpp | 2 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/commands.ci.cpp | 6 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/commands.cpp | 2 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/commands.edit.cpp | 3 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/commands.fetch.cpp | 539 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/commands.hash.cpp | 12 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/commands.integrate.cpp | 125 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/commands.list.cpp | 2 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/export.cpp | 4 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/install.cpp | 43 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/paragraphs.cpp | 4 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/userconfig.cpp | 11 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/vcpkgpaths.cpp | 11 |
22 files changed, 609 insertions, 366 deletions
diff --git a/toolsrc/src/tests.paragraph.cpp b/toolsrc/src/tests.paragraph.cpp index dca89bc59..9a56ad9ee 100644 --- a/toolsrc/src/tests.paragraph.cpp +++ b/toolsrc/src/tests.paragraph.cpp @@ -5,15 +5,6 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework; -namespace Microsoft::VisualStudio::CppUnitTestFramework -{ - template<> - inline std::wstring ToString<vcpkg::PackageSpecParseResult>(const vcpkg::PackageSpecParseResult& t) - { - return ToString(static_cast<uint32_t>(t)); - } -} - namespace Strings = vcpkg::Strings; namespace UnitTest1 diff --git a/toolsrc/src/tests.utils.cpp b/toolsrc/src/tests.utils.cpp index a3d8ffc7d..ac391f559 100644 --- a/toolsrc/src/tests.utils.cpp +++ b/toolsrc/src/tests.utils.cpp @@ -5,36 +5,6 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework; using namespace vcpkg; -namespace Microsoft::VisualStudio::CppUnitTestFramework -{ - std::wstring ToString(const vcpkg::Dependencies::InstallPlanType& t) - { - switch (t) - { - case Dependencies::InstallPlanType::ALREADY_INSTALLED: return L"ALREADY_INSTALLED"; - case Dependencies::InstallPlanType::BUILD_AND_INSTALL: return L"BUILD_AND_INSTALL"; - case Dependencies::InstallPlanType::EXCLUDED: return L"EXCLUDED"; - case Dependencies::InstallPlanType::UNKNOWN: return L"UNKNOWN"; - default: return ToString(static_cast<int>(t)); - } - } - - std::wstring ToString(const vcpkg::Dependencies::RequestType& t) - { - switch (t) - { - case Dependencies::RequestType::AUTO_SELECTED: return L"AUTO_SELECTED"; - case Dependencies::RequestType::USER_REQUESTED: return L"USER_REQUESTED"; - case Dependencies::RequestType::UNKNOWN: return L"UNKNOWN"; - default: return ToString(static_cast<int>(t)); - } - } - - std::wstring ToString(const vcpkg::PackageSpecParseResult& t) { return ToString(static_cast<uint32_t>(t)); } - - std::wstring ToString(const vcpkg::PackageSpec& t) { return ToString(t.to_string()); } -} - std::unique_ptr<StatusParagraph> make_status_pgh(const char* name, const char* depends, const char* default_features, diff --git a/toolsrc/src/vcpkg.cpp b/toolsrc/src/vcpkg.cpp index 06c99e9a8..ac2eec876 100644 --- a/toolsrc/src/vcpkg.cpp +++ b/toolsrc/src/vcpkg.cpp @@ -70,7 +70,7 @@ static void inner(const VcpkgCmdArguments& args) fs::path vcpkg_root_dir; if (args.vcpkg_root_dir != nullptr) { - vcpkg_root_dir = fs::stdfs::absolute(Strings::to_utf16(*args.vcpkg_root_dir)); + vcpkg_root_dir = fs::stdfs::absolute(fs::u8path(*args.vcpkg_root_dir)); } else { @@ -94,6 +94,8 @@ static void inner(const VcpkgCmdArguments& args) Checks::check_exit(VCPKG_LINE_INFO, !vcpkg_root_dir.empty(), "Error: Could not detect vcpkg-root."); + Debug::println("Using vcpkg-root: %s", vcpkg_root_dir.u8string()); + auto default_vs_path = System::get_environment_variable("VCPKG_DEFAULT_VS_PATH").value_or(""); const Expected<VcpkgPaths> expected_paths = VcpkgPaths::create(vcpkg_root_dir, default_vs_path); diff --git a/toolsrc/src/vcpkg/base/checks.cpp b/toolsrc/src/vcpkg/base/checks.cpp index d96cb98ff..2ac5f9a15 100644 --- a/toolsrc/src/vcpkg/base/checks.cpp +++ b/toolsrc/src/vcpkg/base/checks.cpp @@ -16,6 +16,8 @@ namespace vcpkg::Checks const auto elapsed_us = GlobalState::timer.lock()->microseconds(); + Debug::println("Exiting after %d us", static_cast<int>(elapsed_us)); + auto metrics = Metrics::g_metrics.lock(); metrics->track_metric("elapsed_us", elapsed_us); GlobalState::debugging = false; diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp index 1723b467e..3d96e834b 100644 --- a/toolsrc/src/vcpkg/base/files.cpp +++ b/toolsrc/src/vcpkg/base/files.cpp @@ -55,17 +55,18 @@ namespace vcpkg::Files virtual fs::path find_file_recursively_up(const fs::path& starting_dir, const std::string& filename) const override { + static const fs::path UNIX_ROOT = "/"; fs::path current_dir = starting_dir; - for (; !current_dir.empty(); current_dir = current_dir.parent_path()) + for (; !current_dir.empty() && current_dir != UNIX_ROOT; current_dir = current_dir.parent_path()) { const fs::path candidate = current_dir / filename; if (exists(candidate)) { - break; + return current_dir; } } - return current_dir; + return fs::path(); } virtual std::vector<fs::path> get_files_recursive(const fs::path& dir) const override diff --git a/toolsrc/src/vcpkg/base/strings.cpp b/toolsrc/src/vcpkg/base/strings.cpp index fbc33ca42..8d43e7af7 100644 --- a/toolsrc/src/vcpkg/base/strings.cpp +++ b/toolsrc/src/vcpkg/base/strings.cpp @@ -49,33 +49,29 @@ namespace vcpkg::Strings::details namespace vcpkg::Strings { +#if defined(_WIN32) std::wstring to_utf16(const CStringView& s) { -#if defined(_WIN32) std::wstring output; const size_t size = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, nullptr, 0); if (size == 0) return output; output.resize(size - 1); MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, output.data(), static_cast<int>(size) - 1); return output; -#else - Checks::unreachable(VCPKG_LINE_INFO); -#endif } +#endif +#if defined(_WIN32) std::string to_utf8(const wchar_t* w) { -#if defined(_WIN32) std::string output; const size_t size = WideCharToMultiByte(CP_UTF8, 0, w, -1, nullptr, 0, nullptr, nullptr); if (size == 0) return output; output.resize(size - 1); WideCharToMultiByte(CP_UTF8, 0, w, -1, output.data(), static_cast<int>(size) - 1, nullptr, nullptr); return output; -#else - Checks::unreachable(VCPKG_LINE_INFO); -#endif } +#endif std::string escape_string(const CStringView& s, char char_to_escape, char escape_char) { @@ -132,6 +128,12 @@ namespace vcpkg::Strings #endif } + bool ends_with(const std::string& s, StringLiteral pattern) + { + if (s.size() < pattern.size()) return false; + return std::equal(s.end() - pattern.size(), s.end(), pattern.c_str(), pattern.c_str() + pattern.size()); + } + std::string replace_all(std::string&& s, const std::string& search, const std::string& rep) { size_t pos = 0; diff --git a/toolsrc/src/vcpkg/base/system.cpp b/toolsrc/src/vcpkg/base/system.cpp index d4210fe6d..1aa12d0a4 100644 --- a/toolsrc/src/vcpkg/base/system.cpp +++ b/toolsrc/src/vcpkg/base/system.cpp @@ -40,8 +40,9 @@ namespace vcpkg::System if (bytes == 0) std::abort(); return fs::path(buf, buf + bytes); #elif defined(__APPLE__) - uint32_t size = 1024 * 32; - char buf[size] = {}; + static constexpr const uint32_t buff_size = 1024 * 32; + uint32_t size = buff_size; + char buf[buff_size] = {}; bool result = _NSGetExecutablePath(buf, &size); Checks::check_exit(VCPKG_LINE_INFO, result != -1, "Could not determine current executable path."); std::unique_ptr<char> canonicalPath(realpath(buf, NULL)); @@ -128,33 +129,9 @@ namespace vcpkg::System R"("%s" %s -P "%s")", cmake_exe.u8string(), cmd_cmake_pass_variables, cmake_script.generic_u8string()); } - PowershellParameter::PowershellParameter(const CStringView varname, const char* varvalue) - : s(Strings::format(R"(-%s '%s')", varname, varvalue)) - { - } - - PowershellParameter::PowershellParameter(const CStringView varname, const std::string& varvalue) - : PowershellParameter(varname, varvalue.c_str()) - { - } - - PowershellParameter::PowershellParameter(const CStringView varname, const fs::path& path) - : PowershellParameter(varname, path.generic_u8string()) - { - } - - static std::string make_powershell_cmd(const fs::path& script_path, - const std::vector<PowershellParameter>& parameters) - { - const std::string args = Strings::join(" ", parameters, [](auto&& v) { return v.s; }); - - // TODO: switch out ExecutionPolicy Bypass with "Remove Mark Of The Web" code and restore RemoteSigned - return Strings::format( - R"(powershell -NoProfile -ExecutionPolicy Bypass -Command "& {& '%s' %s}")", script_path.u8string(), args); - } - int cmd_execute_clean(const CStringView cmd_line, const std::unordered_map<std::string, std::string>& extra_env) { + auto timer = Chrono::ElapsedTimer::create_started(); #if defined(_WIN32) static const std::string SYSTEM_ROOT = get_environment_variable("SystemRoot").value_or_exit(VCPKG_LINE_INFO); static const std::string SYSTEM_32 = SYSTEM_ROOT + R"(\system32)"; @@ -271,11 +248,14 @@ namespace vcpkg::System DWORD exit_code = 0; GetExitCodeProcess(process_info.hProcess, &exit_code); - Debug::println("CreateProcessW() returned %lu", exit_code); + Debug::println("CreateProcessW() returned %lu after %d us", exit_code, static_cast<int>(timer.microseconds())); return static_cast<int>(exit_code); #else + Debug::println("system(%s)", cmd_line.c_str()); fflush(nullptr); - return system(cmd_line.c_str()); + int rc = system(cmd_line.c_str()); + Debug::println("system() returned %d", rc); + return rc; #endif } @@ -366,71 +346,6 @@ namespace vcpkg::System #endif } - void powershell_execute(const std::string& title, - const fs::path& script_path, - const std::vector<PowershellParameter>& parameters) - { - const std::string cmd = make_powershell_cmd(script_path, parameters); - const int rc = System::cmd_execute(cmd); - - if (rc) - { - System::println(Color::error, - "%s\n" - "Could not run:\n" - " '%s'", - title, - script_path.generic_string()); - - { - auto locked_metrics = Metrics::g_metrics.lock(); - locked_metrics->track_property("error", "powershell script failed"); - locked_metrics->track_property("title", title); - } - - Checks::exit_with_code(VCPKG_LINE_INFO, rc); - } - } - - std::string powershell_execute_and_capture_output(const std::string& title, - const fs::path& script_path, - const std::vector<PowershellParameter>& parameters) - { - const std::string cmd = make_powershell_cmd(script_path, parameters); - auto rc = System::cmd_execute_and_capture_output(cmd); - - if (rc.exit_code) - { - System::println(Color::error, - "%s\n" - "Could not run:\n" - " '%s'\n" - "Error message was:\n" - " %s", - title, - script_path.generic_string(), - rc.output); - - { - auto locked_metrics = Metrics::g_metrics.lock(); - locked_metrics->track_property("error", "powershell script failed"); - locked_metrics->track_property("title", title); - } - - Checks::exit_with_code(VCPKG_LINE_INFO, rc.exit_code); - } - - // Remove newline from all output. - // Powershell returns newlines when it hits the column count of the console. - // For example, this is 80 in cmd on Windows 7. If the expected output is longer than 80 lines, we get - // newlines in-between the data. - // To solve this, we design our interaction with powershell to not depend on newlines, - // and then strip all newlines here. - rc.output = Strings::replace_all(std::move(rc.output), "\n", ""); - - return rc.output; - } - void println() { putchar('\n'); } void print(const CStringView message) { fputs(message.c_str(), stdout); } diff --git a/toolsrc/src/vcpkg/binaryparagraph.cpp b/toolsrc/src/vcpkg/binaryparagraph.cpp index 126c7df97..73ca23df1 100644 --- a/toolsrc/src/vcpkg/binaryparagraph.cpp +++ b/toolsrc/src/vcpkg/binaryparagraph.cpp @@ -24,7 +24,7 @@ namespace vcpkg static const std::string DEFAULTFEATURES = "Default-Features"; } - BinaryParagraph::BinaryParagraph() noexcept = default; + BinaryParagraph::BinaryParagraph() = default; BinaryParagraph::BinaryParagraph(std::unordered_map<std::string, std::string> fields) { diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp index 7a9d35667..b8ccb15bf 100644 --- a/toolsrc/src/vcpkg/build.cpp +++ b/toolsrc/src/vcpkg/build.cpp @@ -217,7 +217,12 @@ namespace vcpkg::Build if (it != toolset.supported_architectures.end()) return it->name; } - Checks::exit_with_message(VCPKG_LINE_INFO, "Unsupported toolchain combination %s", target_architecture); + Checks::exit_with_message(VCPKG_LINE_INFO, + "Unsupported toolchain combination. Target was: %s but supported ones were:\n%s", + target_architecture, + Strings::join(",", toolset.supported_architectures, [](const ToolsetArchOption& t) { + return t.name.c_str(); + })); } std::string make_build_env_cmd(const PreBuildInfo& pre_build_info, const Toolset& toolset) @@ -357,7 +362,7 @@ namespace vcpkg::Build { {"CMD", "BUILD"}, {"PORT", config.scf.core_paragraph->name}, - {"CURRENT_PORT_DIR", config.port_dir / "/."}, + {"CURRENT_PORT_DIR", config.port_dir}, {"TARGET_TRIPLET", spec.triplet().canonical_name()}, {"VCPKG_PLATFORM_TOOLSET", toolset.version.c_str()}, {"VCPKG_USE_HEAD_VERSION", @@ -456,9 +461,9 @@ namespace vcpkg::Build abi_tag_entries.insert(abi_tag_entries.end(), dependency_abis.begin(), dependency_abis.end()); abi_tag_entries.emplace_back( - AbiEntry{"portfile", Commands::Hash::get_file_hash(paths, config.port_dir / "portfile.cmake", "SHA1")}); + AbiEntry{"portfile", Commands::Hash::get_file_hash(fs, config.port_dir / "portfile.cmake", "SHA1")}); abi_tag_entries.emplace_back( - AbiEntry{"control", Commands::Hash::get_file_hash(paths, config.port_dir / "CONTROL", "SHA1")}); + AbiEntry{"control", Commands::Hash::get_file_hash(fs, config.port_dir / "CONTROL", "SHA1")}); abi_tag_entries.emplace_back(AbiEntry{"triplet", pre_build_info.triplet_abi_tag}); @@ -493,7 +498,7 @@ namespace vcpkg::Build const auto abi_file_path = paths.buildtrees / name / (triplet.canonical_name() + ".vcpkg_abi_info.txt"); fs.write_contents(abi_file_path, full_abi_info); - return AbiTagAndFile{Commands::Hash::get_file_hash(paths, abi_file_path, "SHA1"), abi_file_path}; + return AbiTagAndFile{Commands::Hash::get_file_hash(fs, abi_file_path, "SHA1"), abi_file_path}; } System::println( @@ -520,8 +525,8 @@ namespace vcpkg::Build System::cmd_execute_clean(Strings::format( R"("%s" x "%s" -o"%s" -y >nul)", seven_zip_exe.u8string(), archive_path.u8string(), pkg_path.u8string())); #else - System::cmd_execute_clean(Strings::format( - R"(unzip -qq "%s" "-d%s")", archive_path.u8string(), pkg_path.u8string())); + System::cmd_execute_clean( + Strings::format(R"(unzip -qq "%s" "-d%s")", archive_path.u8string(), pkg_path.u8string())); #endif } @@ -537,11 +542,10 @@ namespace vcpkg::Build #if defined(_WIN32) auto&& seven_zip_exe = paths.get_tool_exe(Tools::SEVEN_ZIP); - System::cmd_execute_clean(Strings::format( - R"("%s" a "%s" "%s\*" >nul)", - seven_zip_exe.u8string(), - tmp_archive_path.u8string(), - paths.package_dir(spec).u8string())); + System::cmd_execute_clean(Strings::format(R"("%s" a "%s" "%s\*" >nul)", + seven_zip_exe.u8string(), + tmp_archive_path.u8string(), + paths.package_dir(spec).u8string())); #else System::cmd_execute_clean(Strings::format( R"(cd '%s' && zip --quiet -r '%s' *)", paths.package_dir(spec).u8string(), tmp_archive_path.u8string())); @@ -779,14 +783,12 @@ namespace vcpkg::Build { return it_hash->second; } - auto hash = Commands::Hash::get_file_hash(paths, triplet_file_path, "SHA1"); + auto hash = Commands::Hash::get_file_hash(paths.get_filesystem(), triplet_file_path, "SHA1"); s_hash_cache.emplace(triplet_file_path, hash); return hash; } - else - { - return std::string(); - } + + return std::string(); }(); const auto cmd_launch_cmake = System::make_cmake_cmd(cmake_exe_path, diff --git a/toolsrc/src/vcpkg/commands.cache.cpp b/toolsrc/src/vcpkg/commands.cache.cpp index a9d8ba03c..464f4f9ee 100644 --- a/toolsrc/src/vcpkg/commands.cache.cpp +++ b/toolsrc/src/vcpkg/commands.cache.cpp @@ -61,7 +61,7 @@ namespace vcpkg::Commands::Cache for (const BinaryParagraph& binary_paragraph : binary_paragraphs) { const std::string displayname = binary_paragraph.displayname(); - if (Strings::case_insensitive_ascii_find(displayname, args.command_arguments[0]) == displayname.end()) + if (!Strings::case_insensitive_ascii_contains(displayname, args.command_arguments[0])) { continue; } diff --git a/toolsrc/src/vcpkg/commands.ci.cpp b/toolsrc/src/vcpkg/commands.ci.cpp index 04b42ea00..e2b93dc7e 100644 --- a/toolsrc/src/vcpkg/commands.ci.cpp +++ b/toolsrc/src/vcpkg/commands.ci.cpp @@ -154,8 +154,10 @@ namespace vcpkg::Commands::CI void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) { - Checks::check_exit( - VCPKG_LINE_INFO, GlobalState::g_binary_caching, "The ci command requires binary caching to be enabled."); + if (!GlobalState::g_binary_caching) + { + System::println(System::Color::warning, "Warning: Running ci without binary caching!"); + } const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE); diff --git a/toolsrc/src/vcpkg/commands.cpp b/toolsrc/src/vcpkg/commands.cpp index 8b6ffb3d7..09da57705 100644 --- a/toolsrc/src/vcpkg/commands.cpp +++ b/toolsrc/src/vcpkg/commands.cpp @@ -43,7 +43,7 @@ namespace vcpkg::Commands {"portsdiff", &PortsDiff::perform_and_exit}, {"autocomplete", &Autocomplete::perform_and_exit}, {"hash", &Hash::perform_and_exit}, - // {"fetch", &Fetch::perform_and_exit}, + {"fetch", &Fetch::perform_and_exit}, }; return t; } diff --git a/toolsrc/src/vcpkg/commands.edit.cpp b/toolsrc/src/vcpkg/commands.edit.cpp index b6324565c..2569c2cea 100644 --- a/toolsrc/src/vcpkg/commands.edit.cpp +++ b/toolsrc/src/vcpkg/commands.edit.cpp @@ -78,7 +78,8 @@ namespace vcpkg::Commands::Edit if (Util::Sets::contains(options.switches, OPTION_BUILDTREES)) { return Util::fmap(ports, [&](const std::string& port_name) -> std::string { - return (paths.buildtrees / port_name).u8string(); + const auto buildtrees_current_dir = paths.buildtrees / port_name; + return Strings::format(R"###("%s")###", buildtrees_current_dir.u8string()); }); } diff --git a/toolsrc/src/vcpkg/commands.fetch.cpp b/toolsrc/src/vcpkg/commands.fetch.cpp index 1e31e6bc4..33c5c1dcc 100644 --- a/toolsrc/src/vcpkg/commands.fetch.cpp +++ b/toolsrc/src/vcpkg/commands.fetch.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include <vcpkg/base/checks.h> +#include <vcpkg/base/sortedvector.h> #include <vcpkg/base/strings.h> #include <vcpkg/base/system.h> #include <vcpkg/base/util.h> @@ -19,6 +20,7 @@ namespace vcpkg::Commands::Fetch fs::path exe_path; std::string url; fs::path download_path; + bool is_archive; fs::path tool_dir_path; std::string sha512; }; @@ -41,20 +43,85 @@ namespace vcpkg::Commands::Fetch return result; } - static Optional<std::string> extract_string_between_delimiters(const std::string& input, - const std::string& left_delim, - const std::string& right_delim, - const size_t& starting_offset = 0) + struct VcpkgStringRange { - const size_t from = input.find(left_delim, starting_offset); - if (from == std::string::npos) return nullopt; + VcpkgStringRange() = default; - const size_t substring_start = from + left_delim.length(); + // Implicit by design + VcpkgStringRange(const std::string& s) : begin(s.cbegin()), end(s.cend()) {} - const size_t to = input.find(right_delim, substring_start); - if (from == std::string::npos) return nullopt; + VcpkgStringRange(const std::string::const_iterator begin, const std::string::const_iterator end) + : begin(begin), end(end) + { + } + + std::string::const_iterator begin; + std::string::const_iterator end; + + std::string to_string() const { return std::string(this->begin, this->end); } + }; - return input.substr(substring_start, to - substring_start); + static std::vector<VcpkgStringRange> find_all_enclosed(const VcpkgStringRange& input, + const std::string& left_delim, + const std::string& right_delim) + { + std::string::const_iterator it_left = input.begin; + std::string::const_iterator it_right = input.begin; + + std::vector<VcpkgStringRange> output; + + while (true) + { + it_left = std::search(it_right, input.end, left_delim.cbegin(), left_delim.cend()); + if (it_left == input.end) break; + + it_left += left_delim.length(); + + it_right = std::search(it_left, input.end, right_delim.cbegin(), right_delim.cend()); + if (it_right == input.end) break; + + output.emplace_back(it_left, it_right); + + ++it_right; + } + + return output; + } + + static VcpkgStringRange find_exactly_one_enclosed(const VcpkgStringRange& input, + const std::string& left_tag, + const std::string& right_tag) + { + std::vector<VcpkgStringRange> result = find_all_enclosed(input, left_tag, right_tag); + Checks::check_exit(VCPKG_LINE_INFO, + result.size() == 1, + "Found %d sets of %s.*%s but expected exactly 1, in block:\n%s", + result.size(), + left_tag, + right_tag, + input); + return std::move(result.front()); + } + + static Optional<VcpkgStringRange> find_at_most_one_enclosed(const VcpkgStringRange& input, + const std::string& left_tag, + const std::string& right_tag) + { + std::vector<VcpkgStringRange> result = find_all_enclosed(input, left_tag, right_tag); + Checks::check_exit(VCPKG_LINE_INFO, + result.size() <= 1, + "Found %d sets of %s.*%s but expected at most 1, in block:\n%s", + result.size(), + left_tag, + right_tag, + input); + + if (result.empty()) + { + return nullopt; + } + + return result.front(); } static ToolData parse_tool_data_from_xml(const VcpkgPaths& paths, const std::string& tool) @@ -72,20 +139,6 @@ namespace vcpkg::Commands::Fetch #if defined(_WIN32) || defined(__APPLE__) || defined(__linux__) static const std::string XML_VERSION = "2"; static const fs::path XML_PATH = paths.scripts / "vcpkgTools.xml"; - - const auto get_string_inside_tags = - [](const std::string& input, const std::string& left_delim, const std::string& right_delim) -> std::string { - Optional<std::string> result = extract_string_between_delimiters(input, left_delim, right_delim); - Checks::check_exit(VCPKG_LINE_INFO, - result.has_value(), - "Could not find tag <%s>.*<%s> in %s", - left_delim, - right_delim, - XML_PATH.generic_string()); - - return *result.get(); - }; - static const std::regex XML_VERSION_REGEX{R"###(<tools[\s]+version="([^"]+)">)###"}; static const std::string XML = paths.get_filesystem().read_contents(XML_PATH).value_or_exit(VCPKG_LINE_INFO); std::smatch match_xml_version; @@ -111,14 +164,14 @@ namespace vcpkg::Commands::Fetch tool, XML_PATH.generic_string()); - const std::string tool_data = get_string_inside_tags(XML, match_tool_entry[0], R"(</tool>)"); - - const std::string version_as_string = get_string_inside_tags(tool_data, "<version>", R"(</version>)"); + const std::string tool_data = find_exactly_one_enclosed(XML, match_tool_entry[0], "</tool>").to_string(); + const std::string version_as_string = + find_exactly_one_enclosed(tool_data, "<version>", "</version>").to_string(); const std::string exe_relative_path = - get_string_inside_tags(tool_data, "<exeRelativePath>", R"(</exeRelativePath>)"); - const std::string url = get_string_inside_tags(tool_data, "<url>", R"(</url>)"); - const std::string sha512 = get_string_inside_tags(tool_data, "<sha512>", R"(</sha512>)"); - auto archive_name = extract_string_between_delimiters(tool_data, "<archiveName>", R"(</archiveName>)"); + find_exactly_one_enclosed(tool_data, "<exeRelativePath>", "</exeRelativePath>").to_string(); + const std::string url = find_exactly_one_enclosed(tool_data, "<url>", "</url>").to_string(); + const std::string sha512 = find_exactly_one_enclosed(tool_data, "<sha512>", "</sha512>").to_string(); + auto archive_name = find_at_most_one_enclosed(tool_data, "<archiveName>", "</archiveName>"); const Optional<std::array<int, 3>> version = parse_version_string(version_as_string); Checks::check_exit(VCPKG_LINE_INFO, @@ -128,13 +181,14 @@ namespace vcpkg::Commands::Fetch version_as_string); const std::string tool_dir_name = Strings::format("%s-%s-%s", tool, version_as_string, OS_STRING); - const fs::path tool_dir_path = paths.downloads / "tools" / tool_dir_name; + const fs::path tool_dir_path = paths.tools / tool_dir_name; const fs::path exe_path = tool_dir_path / exe_relative_path; return ToolData{*version.get(), exe_path, url, - paths.downloads / archive_name.value_or(exe_relative_path), + paths.downloads / archive_name.value_or(exe_relative_path).to_string(), + archive_name.has_value(), tool_dir_path, sha512}; #endif @@ -162,51 +216,87 @@ namespace vcpkg::Commands::Fetch actual_version[2] >= expected_version[2])); } - static Optional<fs::path> find_if_has_equal_or_greater_version(const std::vector<fs::path>& candidate_paths, + static Optional<fs::path> find_if_has_equal_or_greater_version(Files::Filesystem& fs, + 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 auto it = Util::find_if(candidate_paths, [&](const fs::path& p) { + if (!fs.exists(p)) return false; 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 *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; - } - -#if !defined(_WIN32) 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, ec); fs.remove_all(to_path_partial, ec); fs.create_directories(to_path_partial, ec); - const auto ext = archive.extension(); +#if defined(_WIN32) + if (ext == ".nupkg") + { + static bool recursion_limiter_sevenzip_old = false; + Checks::check_exit(VCPKG_LINE_INFO, !recursion_limiter_sevenzip_old); + recursion_limiter_sevenzip_old = true; + const auto nuget_exe = get_tool_path(paths, Tools::NUGET); + + const std::string stem = archive.stem().u8string(); + // assuming format of [name].[version in the form d.d.d] + // This assumption may not always hold + std::smatch match; + const bool has_match = std::regex_match(stem, match, std::regex{R"###(^(.+)\.(\d+\.\d+\.\d+)$)###"}); + Checks::check_exit(VCPKG_LINE_INFO, + has_match, + "Could not deduce nuget id and version from filename: %s", + archive.u8string()); + + const std::string nugetid = match[1]; + const std::string version = match[2]; + + const auto code_and_output = System::cmd_execute_and_capture_output(Strings::format( + R"("%s" install %s -Version %s -OutputDirectory "%s" -Source "%s" -nocache -DirectDownload -NonInteractive -ForceEnglishOutput -PackageSaveMode nuspec)", + nuget_exe.u8string(), + nugetid, + version, + to_path_partial.u8string(), + paths.downloads.u8string())); + + Checks::check_exit(VCPKG_LINE_INFO, + code_and_output.exit_code == 0, + "Failed to extract '%s' with message:\n%s", + archive.u8string(), + code_and_output.output); + recursion_limiter_sevenzip_old = false; + } + else + { + static bool recursion_limiter_sevenzip = false; + Checks::check_exit(VCPKG_LINE_INFO, !recursion_limiter_sevenzip); + recursion_limiter_sevenzip = true; + const auto seven_zip = get_tool_path(paths, Tools::SEVEN_ZIP); + const auto code_and_output = System::cmd_execute_and_capture_output(Strings::format( + R"("%s" x "%s" -o"%s" -y)", seven_zip.u8string(), archive.u8string(), to_path_partial.u8string())); + Checks::check_exit(VCPKG_LINE_INFO, + code_and_output.exit_code == 0, + "7zip failed while extracting '%s' with message:\n%s", + archive.u8string(), + code_and_output.output); + recursion_limiter_sevenzip = false; + } +#else if (ext == ".gz" && ext.extension() != ".tar") { const auto code = System::cmd_execute( @@ -223,16 +313,23 @@ namespace vcpkg::Commands::Fetch { Checks::exit_with_message(VCPKG_LINE_INFO, "Unexpected archive extension: %s", ext.u8string()); } +#endif - fs.rename(to_path_partial, to_path); + fs.rename(to_path_partial, to_path, ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Failed to do post-extract rename-in-place.\nfs.rename(%s, %s, %s)", + to_path_partial.u8string(), + to_path.u8string(), + ec.message()); } - static void verify_hash(const VcpkgPaths& paths, + static void verify_hash(const Files::Filesystem& fs, const std::string& url, const fs::path& path, const std::string& sha512) { - const std::string actual_hash = Hash::get_file_hash(paths, path, "SHA512"); + const std::string actual_hash = Hash::get_file_hash(fs, path, "SHA512"); Checks::check_exit(VCPKG_LINE_INFO, sha512 == actual_hash, "File does not have the expected hash:\n" @@ -246,78 +343,165 @@ namespace vcpkg::Commands::Fetch actual_hash); } - static void download_file(const VcpkgPaths& paths, +#if defined(_WIN32) + static void winhttp_download_file(Files::Filesystem& fs, + CStringView target_file_path, + CStringView hostname, + CStringView url_path) + { + // Make sure the directories are present, otherwise fopen_s fails + const auto dir = fs::path(target_file_path.c_str()).parent_path(); + std::error_code ec; + fs.create_directories(dir, ec); + Checks::check_exit(VCPKG_LINE_INFO, !ec, "Could not create directories %s", dir.u8string()); + + FILE* f = nullptr; + const errno_t err = fopen_s(&f, target_file_path.c_str(), "wb"); + Checks::check_exit(VCPKG_LINE_INFO, + !err, + "Could not download https://%s%s. Failed to open file %s. Error code was %s", + hostname, + url_path, + target_file_path, + std::to_string(err)); + + auto hSession = WinHttpOpen( + L"vcpkg/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); + Checks::check_exit(VCPKG_LINE_INFO, hSession, "WinHttpOpen() failed: %d", GetLastError()); + + // Use Windows 10 defaults on Windows 7 + DWORD secure_protocols(WINHTTP_FLAG_SECURE_PROTOCOL_SSL3 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | + WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2); + WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, &secure_protocols, sizeof(secure_protocols)); + + // Specify an HTTP server. + auto hConnect = WinHttpConnect(hSession, Strings::to_utf16(hostname).c_str(), INTERNET_DEFAULT_HTTPS_PORT, 0); + Checks::check_exit(VCPKG_LINE_INFO, hConnect, "WinHttpConnect() failed: %d", GetLastError()); + + // Create an HTTP request handle. + auto hRequest = WinHttpOpenRequest(hConnect, + L"GET", + Strings::to_utf16(url_path).c_str(), + nullptr, + WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, + WINHTTP_FLAG_SECURE); + Checks::check_exit(VCPKG_LINE_INFO, hRequest, "WinHttpOpenRequest() failed: %d", GetLastError()); + + // Send a request. + auto bResults = + WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0); + Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpSendRequest() failed: %d", GetLastError()); + + // End the request. + bResults = WinHttpReceiveResponse(hRequest, NULL); + Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpReceiveResponse() failed: %d", GetLastError()); + + std::vector<char> buf; + + size_t total_downloaded_size = 0; + DWORD dwSize = 0; + do + { + DWORD downloaded_size = 0; + bResults = WinHttpQueryDataAvailable(hRequest, &dwSize); + Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpQueryDataAvailable() failed: %d", GetLastError()); + + if (buf.size() < dwSize) buf.resize(dwSize * 2); + + bResults = WinHttpReadData(hRequest, (LPVOID)buf.data(), dwSize, &downloaded_size); + Checks::check_exit(VCPKG_LINE_INFO, bResults, "WinHttpReadData() failed: %d", GetLastError()); + fwrite(buf.data(), 1, downloaded_size, f); + + total_downloaded_size += downloaded_size; + } while (dwSize > 0); + + WinHttpCloseHandle(hSession); + WinHttpCloseHandle(hConnect); + WinHttpCloseHandle(hRequest); + fflush(f); + fclose(f); + } +#endif + + static void download_file(Files::Filesystem& fs, const std::string& url, const fs::path& download_path, const std::string& sha512) { - Files::Filesystem& fs = paths.get_filesystem(); const std::string download_path_part = download_path.u8string() + ".part"; std::error_code ec; + fs.remove(download_path, ec); fs.remove(download_path_part, ec); - const auto code = System::cmd_execute(Strings::format( - R"(curl -L '%s' --create-dirs --output '%s')", url, download_path_part)); +#if defined(_WIN32) + auto url_no_proto = url.substr(8); // drop https:// + auto path_begin = Util::find(url_no_proto, '/'); + std::string hostname(url_no_proto.begin(), path_begin); + std::string path(path_begin, url_no_proto.end()); + + winhttp_download_file(fs, download_path_part.c_str(), hostname, path); +#else + const auto code = System::cmd_execute( + Strings::format(R"(curl -L '%s' --create-dirs --output '%s')", url, download_path_part)); Checks::check_exit(VCPKG_LINE_INFO, code == 0, "Could not download %s", url); +#endif - verify_hash(paths, url, download_path_part, sha512); - fs.rename(download_path_part, download_path); + verify_hash(fs, url, download_path_part, sha512); + fs.rename(download_path_part, download_path, ec); + Checks::check_exit(VCPKG_LINE_INFO, + !ec, + "Failed to do post-download rename-in-place.\nfs.rename(%s, %s, %s)", + download_path_part, + download_path.u8string(), + ec.message()); } -#endif static fs::path fetch_tool(const VcpkgPaths& paths, const std::string& tool_name, const ToolData& tool_data) { const std::array<int, 3>& version = tool_data.version; - const std::string version_as_string = Strings::format("%d.%d.%d", version[0], version[1], version[2]); + Checks::check_exit(VCPKG_LINE_INFO, + !tool_data.url.empty(), + "A suitable version of %s was not found (required v%s) and unable to automatically " + "download a portable one. Please install a newer version of git.", + tool_name, + version_as_string); 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 = paths.scripts / "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 - const auto& fs = paths.get_filesystem(); + auto& fs = paths.get_filesystem(); if (!fs.exists(tool_data.download_path)) { System::println("Downloading %s...", tool_name); - download_file(paths, tool_data.url, tool_data.download_path, tool_data.sha512); + download_file(fs, tool_data.url, tool_data.download_path, tool_data.sha512); System::println("Downloading %s... done.", tool_name); } else { - verify_hash(paths, tool_data.url, tool_data.download_path, tool_data.sha512); + verify_hash(fs, tool_data.url, tool_data.download_path, tool_data.sha512); } - System::println("Extracting %s...", tool_name); - extract_archive(paths, tool_data.download_path, tool_data.tool_dir_path); - System::println("Extracting %s... done.", tool_name); + if (tool_data.is_archive) + { + System::println("Extracting %s...", tool_name); + extract_archive(paths, tool_data.download_path, tool_data.tool_dir_path); + System::println("Extracting %s... done.", tool_name); + } + else + { + std::error_code ec; + fs.create_directories(tool_data.exe_path.parent_path(), ec); + fs.rename(tool_data.download_path, tool_data.exe_path, ec); + } Checks::check_exit(VCPKG_LINE_INFO, fs.exists(tool_data.exe_path), - "Expected %s to exist after extracting", - tool_data.exe_path); + "Expected %s to exist after fetching", + tool_data.exe_path.u8string()); return tool_data.exe_path; -#endif } static fs::path get_cmake_path(const VcpkgPaths& paths) @@ -339,8 +523,8 @@ namespace vcpkg::Commands::Fetch 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.version); + const Optional<fs::path> path = find_if_has_equal_or_greater_version( + paths.get_filesystem(), candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.version); if (const auto p = path.get()) { return *p; @@ -372,7 +556,8 @@ namespace vcpkg::Commands::Fetch 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.version); + auto path = find_if_has_equal_or_greater_version( + paths.get_filesystem(), candidate_paths, "--version", TOOL_DATA.version); if (const auto p = path.get()) { return *p; @@ -390,7 +575,8 @@ namespace vcpkg::Commands::Fetch 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.version); + auto path = + find_if_has_equal_or_greater_version(paths.get_filesystem(), candidate_paths, "", TOOL_DATA.version); if (const auto p = path.get()) { return *p; @@ -401,11 +587,7 @@ namespace vcpkg::Commands::Fetch 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; @@ -420,8 +602,8 @@ namespace vcpkg::Commands::Fetch 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.version); + const Optional<fs::path> path = find_if_has_equal_or_greater_version( + paths.get_filesystem(), candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.version); if (const auto p = path.get()) { return *p; @@ -446,8 +628,8 @@ namespace vcpkg::Commands::Fetch // 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.version); + const Optional<fs::path> path = find_if_has_equal_or_greater_version( + paths.get_filesystem(), candidate_paths, VERSION_CHECK_ARGUMENTS, TOOL_DATA.version); if (const auto p = path.get()) { return *p; @@ -458,48 +640,114 @@ namespace vcpkg::Commands::Fetch struct VisualStudioInstance { + enum class ReleaseType + { + STABLE, + PRERELEASE, + LEGACY + }; + + static bool preferred_first_comparator(const VisualStudioInstance& left, const VisualStudioInstance& right) + { + const auto get_preference_weight = [](const ReleaseType& type) -> int { + switch (type) + { + case ReleaseType::STABLE: return 3; + case ReleaseType::PRERELEASE: return 2; + case ReleaseType::LEGACY: return 1; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + }; + + if (left.release_type != right.release_type) + { + return get_preference_weight(left.release_type) > get_preference_weight(right.release_type); + } + + return left.version > right.version; + } + + VisualStudioInstance(fs::path&& root_path, std::string&& version, const ReleaseType& release_type) + : root_path(std::move(root_path)), version(std::move(version)), release_type(release_type) + { + } + fs::path root_path; std::string version; - std::string release_type; - std::string preference_weight; // Mostly unused, just for verification that order is as intended + ReleaseType release_type; std::string major_version() const { return version.substr(0, 2); } }; static std::vector<VisualStudioInstance> get_visual_studio_instances(const VcpkgPaths& paths) { - const fs::path script = paths.scripts / "findVisualStudioInstallationInstances.ps1"; - const std::string output = - System::powershell_execute_and_capture_output("Detecting Visual Studio instances", script); + const auto& fs = paths.get_filesystem(); + std::vector<VisualStudioInstance> instances; - 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); + const auto& program_files_32_bit = System::get_program_files_32_bit().value_or_exit(VCPKG_LINE_INFO); - std::vector<VisualStudioInstance> instances; - for (const std::string& instance_as_string : instances_as_strings) + // Instances from vswhere + const fs::path vswhere_exe = program_files_32_bit / "Microsoft Visual Studio" / "Installer" / "vswhere.exe"; + if (fs.exists(vswhere_exe)) { - const std::vector<std::string> split = Strings::split(instance_as_string, "::"); + const auto code_and_output = System::cmd_execute_and_capture_output( + Strings::format(R"("%s" -prerelease -legacy -products * -format xml)", vswhere_exe.u8string())); Checks::check_exit(VCPKG_LINE_INFO, - 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)}); + code_and_output.exit_code == 0, + "Running vswhere.exe failed with message:\n%s", + code_and_output.output); + + const auto instance_entries = find_all_enclosed(code_and_output.output, "<instance>", "</instance>"); + for (const VcpkgStringRange& instance : instance_entries) + { + auto maybe_is_prerelease = find_at_most_one_enclosed(instance, "<isPrerelease>", "</isPrerelease>"); + + VisualStudioInstance::ReleaseType release_type = VisualStudioInstance::ReleaseType::LEGACY; + if (const auto p = maybe_is_prerelease.get()) + { + const auto s = p->to_string(); + if (s == "0") + release_type = VisualStudioInstance::ReleaseType::STABLE; + else if (s == "1") + release_type = VisualStudioInstance::ReleaseType::PRERELEASE; + else + Checks::unreachable(VCPKG_LINE_INFO); + } + + instances.emplace_back( + find_exactly_one_enclosed(instance, "<installationPath>", "</installationPath>").to_string(), + find_exactly_one_enclosed(instance, "<installationVersion>", "</installationVersion>").to_string(), + release_type); + } + } + + const auto append_if_has_cl = [&](fs::path&& path_root) { + const auto cl_exe = path_root / "VC" / "bin" / "cl.exe"; + const auto vcvarsall_bat = path_root / "VC" / "vcvarsall.bat"; + + if (fs.exists(cl_exe) && fs.exists(vcvarsall_bat)) + instances.emplace_back(std::move(path_root), "14.0", VisualStudioInstance::ReleaseType::LEGACY); + }; + + // VS2015 instance from environment variable + auto maybe_vs140_comntools = System::get_environment_variable("vs140comntools"); + if (const auto path_as_string = maybe_vs140_comntools.get()) + { + // We want lexically_normal(), but it is not available + // Correct root path might be 2 or 3 levels up, depending on if the path has trailing backslash. Try both. + auto common7_tools = fs::path{*path_as_string}; + append_if_has_cl(fs::path{*path_as_string}.parent_path().parent_path()); + append_if_has_cl(fs::path{*path_as_string}.parent_path().parent_path().parent_path()); } + // VS2015 instance from Program Files + append_if_has_cl(program_files_32_bit / "Microsoft Visual Studio 14.0"); + return instances; } - std::vector<Toolset> find_toolset_instances(const VcpkgPaths& paths) +#if defined(_WIN32) + std::vector<Toolset> find_toolset_instances_preferred_first(const VcpkgPaths& paths) { using CPU = System::CPUArchitecture; @@ -511,12 +759,14 @@ namespace vcpkg::Commands::Fetch 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) { + const SortedVector<VisualStudioInstance> sorted{get_visual_studio_instances(paths), + VisualStudioInstance::preferred_first_comparator}; + + const bool v140_is_available = Util::find_if(sorted, [&](const VisualStudioInstance& vs_instance) { return vs_instance.major_version() == "14"; - }) != vs_instances.cend(); + }) != sorted.end(); - for (const VisualStudioInstance& vs_instance : vs_instances) + for (const VisualStudioInstance& vs_instance : sorted) { const std::string major_version = vs_instance.major_version(); if (major_version == "15") @@ -566,7 +816,7 @@ namespace vcpkg::Commands::Fetch paths_examined.push_back(dumpbin_path); if (fs.exists(dumpbin_path)) { - const Toolset v141toolset = Toolset{ + const Toolset v141toolset{ vs_instance.root_path, dumpbin_path, vcvarsall_bat, {}, V_141, supported_architectures}; auto english_language_pack = dumpbin_path.parent_path() / "1033"; @@ -581,12 +831,12 @@ namespace vcpkg::Commands::Fetch if (v140_is_available) { - const Toolset v140toolset = Toolset{vs_instance.root_path, - dumpbin_path, - vcvarsall_bat, - {"-vcvars_ver=14.0"}, - V_140, - supported_architectures}; + const Toolset v140toolset{vs_instance.root_path, + dumpbin_path, + vcvarsall_bat, + {"-vcvars_ver=14.0"}, + V_140, + supported_architectures}; found_toolsets.push_back(v140toolset); } @@ -670,6 +920,7 @@ namespace vcpkg::Commands::Fetch return found_toolsets; } +#endif fs::path get_tool_path(const VcpkgPaths& paths, const std::string& tool) { diff --git a/toolsrc/src/vcpkg/commands.hash.cpp b/toolsrc/src/vcpkg/commands.hash.cpp index 1f709f87b..0c7aa72c4 100644 --- a/toolsrc/src/vcpkg/commands.hash.cpp +++ b/toolsrc/src/vcpkg/commands.hash.cpp @@ -154,10 +154,9 @@ namespace vcpkg::Commands::Hash }; } - std::string get_file_hash(const VcpkgPaths& paths, const fs::path& path, const std::string& hash_type) + std::string get_file_hash(const Files::Filesystem& fs, const fs::path& path, const std::string& hash_type) { - Checks::check_exit( - VCPKG_LINE_INFO, paths.get_filesystem().exists(path), "File %s does not exist", path.u8string()); + Checks::check_exit(VCPKG_LINE_INFO, fs.exists(path), "File %s does not exist", path.u8string()); return BCryptHasher{hash_type}.hash_file(path); } @@ -202,11 +201,10 @@ namespace vcpkg::Commands::Hash return split[0]; } - std::string get_file_hash(const VcpkgPaths& paths, const fs::path& path, const std::string& hash_type) + std::string get_file_hash(const Files::Filesystem& fs, const fs::path& path, const std::string& hash_type) { const std::string digest_size = get_digest_size(hash_type); - Checks::check_exit( - VCPKG_LINE_INFO, paths.get_filesystem().exists(path), "File %s does not exist", path.u8string()); + Checks::check_exit(VCPKG_LINE_INFO, fs.exists(path), "File %s does not exist", path.u8string()); const std::string cmd_line = Strings::format(R"(shasum -a %s "%s")", digest_size, path.u8string()); return run_shasum_and_post_process(cmd_line); } @@ -239,7 +237,7 @@ namespace vcpkg::Commands::Hash const fs::path file_to_hash = args.command_arguments[0]; const std::string algorithm = args.command_arguments.size() == 2 ? args.command_arguments[1] : "SHA512"; - const std::string hash = get_file_hash(paths, file_to_hash, algorithm); + const std::string hash = get_file_hash(paths.get_filesystem(), file_to_hash, algorithm); System::println(hash); Checks::exit_success(VCPKG_LINE_INFO); } diff --git a/toolsrc/src/vcpkg/commands.integrate.cpp b/toolsrc/src/vcpkg/commands.integrate.cpp index 7061a3984..82172e363 100644 --- a/toolsrc/src/vcpkg/commands.integrate.cpp +++ b/toolsrc/src/vcpkg/commands.integrate.cpp @@ -5,6 +5,8 @@ #include <vcpkg/base/system.h> #include <vcpkg/base/util.h> #include <vcpkg/commands.h> +#include <vcpkg/metrics.h> +#include <vcpkg/userconfig.h> namespace vcpkg::Commands::Integrate { @@ -156,8 +158,10 @@ namespace vcpkg::Commands::Integrate } #endif + static fs::path get_path_txt_path() { return get_user_dir() / "vcpkg.path.txt"; } + #if defined(_WIN32) - static void integrate_install(const VcpkgPaths& paths) + static void integrate_install_msbuild14(Files::Filesystem& fs, const fs::path& tmp_dir) { static const std::array<fs::path, 2> OLD_SYSTEM_TARGET_FILES = { System::get_program_files_32_bit().value_or_exit(VCPKG_LINE_INFO) / @@ -168,8 +172,6 @@ namespace vcpkg::Commands::Integrate System::get_program_files_32_bit().value_or_exit(VCPKG_LINE_INFO) / "MSBuild/Microsoft.Cpp/v4.0/V140/ImportBefore/Default/vcpkg.system.props"; - auto& fs = paths.get_filesystem(); - // TODO: This block of code should eventually be removed for (auto&& old_system_wide_targets_file : OLD_SYSTEM_TARGET_FILES) { @@ -188,12 +190,6 @@ namespace vcpkg::Commands::Integrate } } } - - std::error_code ec; - const fs::path tmp_dir = paths.buildsystems / "tmp"; - fs.create_directory(paths.buildsystems, ec); - fs.create_directory(tmp_dir, ec); - bool should_install_system = true; const Expected<std::string> system_wide_file_contents = fs.read_contents(SYSTEM_WIDE_TARGETS_FILE); static const std::regex RE(R"###(<!-- version (\d+) -->)###"); @@ -232,24 +228,52 @@ namespace vcpkg::Commands::Integrate "Error: failed to copy targets file to %s", SYSTEM_WIDE_TARGETS_FILE.string()); } + } +#endif - const fs::path appdata_src_path = tmp_dir / "vcpkg.user.targets"; - fs.write_contents(appdata_src_path, - create_appdata_targets_shortcut(paths.buildsystems_msbuild_targets.string())); - auto appdata_dst_path = get_appdata_targets_path(); + static void integrate_install(const VcpkgPaths& paths) + { + auto& fs = paths.get_filesystem(); - const auto rc = fs.copy_file(appdata_src_path, appdata_dst_path, fs::copy_options::overwrite_existing, ec); +#if defined(_WIN32) + { + std::error_code ec; + const fs::path tmp_dir = paths.buildsystems / "tmp"; + fs.create_directory(paths.buildsystems, ec); + fs.create_directory(tmp_dir, ec); - if (!rc || ec) + integrate_install_msbuild14(fs, tmp_dir); + + const fs::path appdata_src_path = tmp_dir / "vcpkg.user.targets"; + fs.write_contents(appdata_src_path, + create_appdata_targets_shortcut(paths.buildsystems_msbuild_targets.string())); + auto appdata_dst_path = get_appdata_targets_path(); + + const auto rc = fs.copy_file(appdata_src_path, appdata_dst_path, fs::copy_options::overwrite_existing, ec); + + if (!rc || ec) + { + System::println(System::Color::error, + "Error: Failed to copy file: %s -> %s", + appdata_src_path.string(), + appdata_dst_path.string()); + Checks::exit_fail(VCPKG_LINE_INFO); + } + } +#endif + + const auto pathtxt = get_path_txt_path(); + std::error_code ec; + fs.write_contents(pathtxt, paths.root.generic_u8string(), ec); + if (ec) { - System::println(System::Color::error, - "Error: Failed to copy file: %s -> %s", - appdata_src_path.string(), - appdata_dst_path.string()); + System::println(System::Color::error, "Error: Failed to write file: %s", pathtxt.string()); Checks::exit_fail(VCPKG_LINE_INFO); } + System::println(System::Color::success, "Applied user-wide integration for this vcpkg root."); const fs::path cmake_toolchain = paths.buildsystems / "vcpkg.cmake"; +#if defined(_WIN32) System::println( R"( All MSBuild C++ projects can now #include any installed libraries. @@ -258,17 +282,29 @@ Installing new libraries will make them instantly available. CMake projects should use: "-DCMAKE_TOOLCHAIN_FILE=%s")", cmake_toolchain.generic_string()); +#else + System::println( + R"( +CMake projects should use: "-DCMAKE_TOOLCHAIN_FILE=%s")", + cmake_toolchain.generic_string()); +#endif Checks::exit_success(VCPKG_LINE_INFO); } static void integrate_remove(Files::Filesystem& fs) { + std::error_code ec; + bool was_deleted = false; + +#if defined(_WIN32) const fs::path path = get_appdata_targets_path(); - std::error_code ec; - const bool was_deleted = fs.remove(path, ec); + was_deleted |= fs.remove(path, ec); + Checks::check_exit(VCPKG_LINE_INFO, !ec, "Error: Unable to remove user-wide integration: %s", ec.message()); +#endif + was_deleted |= fs.remove(get_path_txt_path(), ec); Checks::check_exit(VCPKG_LINE_INFO, !ec, "Error: Unable to remove user-wide integration: %s", ec.message()); if (was_deleted) @@ -282,7 +318,6 @@ CMake projects should use: "-DCMAKE_TOOLCHAIN_FILE=%s")", Checks::exit_success(VCPKG_LINE_INFO); } -#endif #if defined(WIN32) static void integrate_project(const VcpkgPaths& paths) @@ -335,6 +370,43 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console #endif #if defined(_WIN32) + static void integrate_powershell(const VcpkgPaths& paths) + { + static constexpr StringLiteral TITLE = "PowerShell Tab-Completion"; + const fs::path script_path = paths.scripts / "addPoshVcpkgToPowershellProfile.ps1"; + + // Console font corruption workaround + SetConsoleCP(437); + SetConsoleOutputCP(437); + + const std::string cmd = Strings::format( + R"(powershell -NoProfile -ExecutionPolicy Bypass -Command "& {& '%s' %s}")", script_path.u8string(), ""); + const int rc = System::cmd_execute(cmd); + + SetConsoleCP(CP_UTF8); + SetConsoleOutputCP(CP_UTF8); + + if (rc) + { + System::println(System::Color::error, + "%s\n" + "Could not run:\n" + " '%s'", + TITLE, + script_path.generic_string()); + + { + auto locked_metrics = Metrics::g_metrics.lock(); + locked_metrics->track_property("error", "powershell script failed"); + locked_metrics->track_property("title", TITLE); + } + } + + Checks::exit_with_code(VCPKG_LINE_INFO, rc); + } +#endif + +#if defined(_WIN32) const char* const INTEGRATE_COMMAND_HELPSTRING = " vcpkg integrate install Make installed packages available user-wide. Requires admin privileges on " "first use\n" @@ -343,7 +415,8 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console " vcpkg integrate powershell Enable PowerShell Tab-Completion\n"; #else const char* const INTEGRATE_COMMAND_HELPSTRING = - "No user-wide integration methods are available on this platform\n"; + " vcpkg integrate install Make installed packages available user-wide.\n" + " vcpkg integrate remove Remove user-wide integration\n"; #endif namespace Subcommand @@ -373,7 +446,6 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console { Util::unused(args.parse_arguments(COMMAND_STRUCTURE)); -#if defined(_WIN32) if (args.command_arguments[0] == Subcommand::INSTALL) { return integrate_install(paths); @@ -382,15 +454,14 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console { return integrate_remove(paths.get_filesystem()); } +#if defined(_WIN32) if (args.command_arguments[0] == Subcommand::PROJECT) { return integrate_project(paths); } if (args.command_arguments[0] == Subcommand::POWERSHELL) { - System::powershell_execute("PowerShell Tab-Completion", - paths.scripts / "addPoshVcpkgToPowershellProfile.ps1"); - Checks::exit_success(VCPKG_LINE_INFO); + return integrate_powershell(paths); } #endif diff --git a/toolsrc/src/vcpkg/commands.list.cpp b/toolsrc/src/vcpkg/commands.list.cpp index 1bfbc4247..cadc06ad3 100644 --- a/toolsrc/src/vcpkg/commands.list.cpp +++ b/toolsrc/src/vcpkg/commands.list.cpp @@ -76,7 +76,7 @@ namespace vcpkg::Commands::List for (const StatusParagraph* status_paragraph : installed_packages) { const std::string displayname = status_paragraph->package.displayname(); - if (Strings::case_insensitive_ascii_find(displayname, args.command_arguments[0]) == displayname.end()) + if (!Strings::case_insensitive_ascii_contains(displayname, args.command_arguments[0])) { continue; } diff --git a/toolsrc/src/vcpkg/export.cpp b/toolsrc/src/vcpkg/export.cpp index 152252018..c444cf3d0 100644 --- a/toolsrc/src/vcpkg/export.cpp +++ b/toolsrc/src/vcpkg/export.cpp @@ -226,10 +226,6 @@ namespace vcpkg::Export {fs::path{"scripts"} / "buildsystems" / "msbuild" / "vcpkg.targets"}, {fs::path{"scripts"} / "buildsystems" / "vcpkg.cmake"}, {fs::path{"scripts"} / "cmake" / "vcpkg_get_windows_sdk.cmake"}, - {fs::path{"scripts"} / "getWindowsSDK.ps1"}, - {fs::path{"scripts"} / "getProgramFilesPlatformBitness.ps1"}, - {fs::path{"scripts"} / "getProgramFiles32bit.ps1"}, - {fs::path{"scripts"} / "VcpkgPowershellUtils.ps1"}, }; for (const fs::path& file : integration_files_relative_to_root) diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp index fc336d6c7..40e696fa0 100644 --- a/toolsrc/src/vcpkg/install.cpp +++ b/toolsrc/src/vcpkg/install.cpp @@ -462,14 +462,14 @@ namespace vcpkg::Install auto files = fs.read_lines(paths.listfile_path(bpgh)); if (auto p_lines = files.get()) { + std::map<std::string, std::string> config_files; std::map<std::string, std::vector<std::string>> library_targets; for (auto&& suffix : *p_lines) { - if (Strings::case_insensitive_ascii_find(suffix, "/share/") != suffix.end() && - suffix.substr(suffix.size() - 6) == ".cmake") + if (Strings::case_insensitive_ascii_contains(suffix, "/share/") && Strings::ends_with(suffix, ".cmake")) { - // File is inside the share folder + // CMake file is inside the share folder auto path = paths.installed / suffix; auto maybe_contents = fs.read_contents(path); auto find_package_name = path.parent_path().filename().u8string(); @@ -485,6 +485,21 @@ namespace vcpkg::Install ++next; } } + + auto filename = fs::u8path(suffix).filename().u8string(); + + if (Strings::ends_with(filename, "Config.cmake")) + { + auto root = filename.substr(0, filename.size() - 12); + if (Strings::case_insensitive_ascii_equals(root, find_package_name)) + config_files[find_package_name] = root; + } + else if (Strings::ends_with(filename, "-config.cmake")) + { + auto root = filename.substr(0, filename.size() - 13); + if (Strings::case_insensitive_ascii_equals(root, find_package_name)) + config_files[find_package_name] = root; + } } } @@ -497,11 +512,23 @@ namespace vcpkg::Install for (auto&& library_target_pair : library_targets) { + auto config_it = config_files.find(library_target_pair.first); + if (config_it != config_files.end()) + System::println(" find_package(%s CONFIG REQUIRED)", config_it->second); + else + System::println(" find_package(%s CONFIG REQUIRED)", library_target_pair.first); + + std::sort(library_target_pair.second.begin(), + library_target_pair.second.end(), + [](const std::string& l, const std::string& r) { + if (l.size() < r.size()) return true; + if (l.size() > r.size()) return false; + return l < r; + }); + if (library_target_pair.second.size() <= 4) { - System::println(" find_package(%s REQUIRED)\n" - " target_link_libraries(main PRIVATE %s)\n", - library_target_pair.first, + System::println(" target_link_libraries(main PRIVATE %s)\n", Strings::join(" ", library_target_pair.second)); } else @@ -509,10 +536,8 @@ namespace vcpkg::Install auto omitted = library_target_pair.second.size() - 4; library_target_pair.second.erase(library_target_pair.second.begin() + 4, library_target_pair.second.end()); - System::println(" find_package(%s REQUIRED)\n" - " # Note: %zd targets were omitted\n" + System::println(" # Note: %zd target(s) were omitted.\n" " target_link_libraries(main PRIVATE %s)\n", - library_target_pair.first, omitted, Strings::join(" ", library_target_pair.second)); } diff --git a/toolsrc/src/vcpkg/paragraphs.cpp b/toolsrc/src/vcpkg/paragraphs.cpp index 41ffceec7..77c028937 100644 --- a/toolsrc/src/vcpkg/paragraphs.cpp +++ b/toolsrc/src/vcpkg/paragraphs.cpp @@ -253,6 +253,10 @@ namespace vcpkg::Paragraphs LoadResults ret; auto port_dirs = fs.get_files_non_recursive(ports_dir); Util::sort(port_dirs); + Util::erase_remove_if(port_dirs, [&](auto&& port_dir_entry) { + return fs.is_regular_file(port_dir_entry) && port_dir_entry.filename() == ".DS_Store"; + }); + for (auto&& path : port_dirs) { auto maybe_spgh = try_load_port(fs, path); diff --git a/toolsrc/src/vcpkg/userconfig.cpp b/toolsrc/src/vcpkg/userconfig.cpp index 574a97b64..4945fdaaa 100644 --- a/toolsrc/src/vcpkg/userconfig.cpp +++ b/toolsrc/src/vcpkg/userconfig.cpp @@ -29,16 +29,21 @@ namespace namespace vcpkg { - static fs::path get_config_path() + fs::path get_user_dir() { #if defined(_WIN32) - return get_localappdata() / "vcpkg" / "config"; + return get_localappdata() / "vcpkg"; #else auto maybe_home = System::get_environment_variable("HOME"); - return fs::path(maybe_home.value_or("/var")) / ".vcpkg" / "config"; + return fs::path(maybe_home.value_or("/var")) / ".vcpkg"; #endif } + static fs::path get_config_path() + { + return get_user_dir() / "config"; + } + UserConfig UserConfig::try_read_data(const Files::Filesystem& fs) { UserConfig ret; diff --git a/toolsrc/src/vcpkg/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp index 0903c2d76..9b74bea74 100644 --- a/toolsrc/src/vcpkg/vcpkgpaths.cpp +++ b/toolsrc/src/vcpkg/vcpkgpaths.cpp @@ -39,6 +39,7 @@ namespace vcpkg paths.triplets = paths.root / "triplets"; paths.scripts = paths.root / "scripts"; + paths.tools = paths.downloads / "tools"; paths.buildsystems = paths.scripts / "buildsystems"; paths.buildsystems_msbuild_targets = paths.buildsystems / "msbuild" / "vcpkg.targets"; @@ -113,9 +114,11 @@ namespace vcpkg return external_toolset; } - // Invariant: toolsets are non-empty and sorted with newest at back() - const std::vector<Toolset>& vs_toolsets = - this->toolsets.get_lazy([this]() { return Commands::Fetch::find_toolset_instances(*this); }); +#if !defined(_WIN32) + Checks::exit_with_message(VCPKG_LINE_INFO, "Cannot build windows triplets from non-windows."); +#else + const std::vector<Toolset>& vs_toolsets = this->toolsets.get_lazy( + [this]() { return Commands::Fetch::find_toolset_instances_preferred_first(*this); }); std::vector<const Toolset*> candidates = Util::element_pointers(vs_toolsets); const auto tsv = prebuildinfo.platform_toolset.get(); @@ -159,6 +162,8 @@ namespace vcpkg Checks::check_exit(VCPKG_LINE_INFO, !candidates.empty(), "No suitable Visual Studio instances were found"); return *candidates.front(); + +#endif } Files::Filesystem& VcpkgPaths::get_filesystem() const { return Files::get_real_filesystem(); } |
