diff --git a/include/vcpkg/base/message-data.inc.h b/include/vcpkg/base/message-data.inc.h index 168d21f128..ad452539f8 100644 --- a/include/vcpkg/base/message-data.inc.h +++ b/include/vcpkg/base/message-data.inc.h @@ -280,10 +280,6 @@ DECLARE_MESSAGE(BaselineFileNoDefaultField, (msg::commit_sha), "", "The baseline file at commit {commit_sha} was invalid (no \"default\" field).") -DECLARE_MESSAGE(BaselineFileNoDefaultFieldPath, - (msg::path), - "", - "baseline file at {path} was invalid (no \"default\" field)") DECLARE_MESSAGE(BaselineGitShowFailed, (msg::commit_sha), "", @@ -297,11 +293,6 @@ DECLARE_MESSAGE(BaselineMissing, "git add versions\n" "git commit -m \"Update version database\"\n" "to set {version} as the baseline version.") -DECLARE_MESSAGE(BaselineMissingDefault, - (msg::commit_sha, msg::url), - "", - "The baseline.json from commit `\"{commit_sha}\"` in the repo {url} was invalid (did not " - "contain a \"default\" field).") DECLARE_MESSAGE(BinaryCacheVendorHTTP, (), "", "HTTP servers") DECLARE_MESSAGE(BinarySourcesArg, (), @@ -911,10 +902,6 @@ DECLARE_MESSAGE(CouldNotDeduceNugetIdAndVersion, (msg::path), "", "Could not deduce nuget id and version from filename: {path}") -DECLARE_MESSAGE(CouldNotFindBaseline, - (msg::commit_sha, msg::path), - "", - "Could not find explicitly specified baseline `\"{commit_sha}\"` in baseline file {path}") DECLARE_MESSAGE(CouldNotFindBaselineInCommit, (msg::url, msg::commit_sha, msg::package_name), "", @@ -1223,7 +1210,6 @@ DECLARE_MESSAGE(FailedToDetermineCurrentCommit, (), "", "Failed to determine the DECLARE_MESSAGE(FailedToDownloadFromMirrorSet, (), "", "Failed to download from mirror set") DECLARE_MESSAGE(FailedToExtract, (msg::path), "", "Failed to extract \"{path}\":") DECLARE_MESSAGE(FailedToFetchRepo, (msg::url), "", "Failed to fetch {url}.") -DECLARE_MESSAGE(FailedToFindBaseline, (), "", "Failed to find baseline.json") DECLARE_MESSAGE(FailedToFindPortFeature, (msg::feature, msg::package_name), "", diff --git a/include/vcpkg/paragraphparser.h b/include/vcpkg/paragraphparser.h index 0ba8eb7b55..984e594275 100644 --- a/include/vcpkg/paragraphparser.h +++ b/include/vcpkg/paragraphparser.h @@ -48,16 +48,10 @@ namespace vcpkg template using ParseExpected = vcpkg::ExpectedT, std::unique_ptr>; - template - ExpectedL

map_parse_expected_to_localized_string(ParseExpected

&& parse_expected) + static constexpr struct ToLocalizedString_t { - if (auto value = parse_expected.get()) - { - return std::move(**value); - } - - return LocalizedString::from_raw(parse_expected.error()->to_string()); - } + LocalizedString operator()(std::unique_ptr p) const; + } ToLocalizedString; using Paragraph = std::map, std::less<>>; diff --git a/include/vcpkg/paragraphs.h b/include/vcpkg/paragraphs.h index 9718544f72..8e12aa80c4 100644 --- a/include/vcpkg/paragraphs.h +++ b/include/vcpkg/paragraphs.h @@ -5,9 +5,13 @@ #include #include +#include #include +#include +#include + namespace vcpkg::Paragraphs { uint64_t get_load_ports_stats(); @@ -22,11 +26,20 @@ namespace vcpkg::Paragraphs bool is_port_directory(const ReadOnlyFilesystem& fs, const Path& maybe_directory); - ParseExpected try_load_port(const ReadOnlyFilesystem& fs, const Path& port_directory); - ParseExpected try_load_port_text(const std::string& text, - StringView origin, - bool is_manifest, - MessageSink& warning_sink); + // If an error occurs, the Expected will be in the error state. + // Otherwise, if the port is known, the unique_ptr contains the loaded port information. + // Otherwise, the unique_ptr is nullptr. + ExpectedL> try_load_port(const ReadOnlyFilesystem& fs, + StringView port_name, + const Path& port_directory); + // Identical to try_load_port, but the port unknown condition is mapped to an error. + ExpectedL> try_load_port_required(const ReadOnlyFilesystem& fs, + StringView port_name, + const Path& port_directory); + ExpectedL> try_load_port_text(const std::string& text, + StringView origin, + bool is_manifest, + MessageSink& warning_sink); ExpectedL try_load_cached_package(const ReadOnlyFilesystem& fs, const Path& package_dir, @@ -35,7 +48,7 @@ namespace vcpkg::Paragraphs struct LoadResults { std::vector paragraphs; - std::vector> errors; + std::vector> errors; }; LoadResults try_load_all_registry_ports(const ReadOnlyFilesystem& fs, const RegistrySet& registries); diff --git a/include/vcpkg/registries.h b/include/vcpkg/registries.h index 6674f2ae44..00c4996d90 100644 --- a/include/vcpkg/registries.h +++ b/include/vcpkg/registries.h @@ -76,19 +76,24 @@ namespace vcpkg { virtual StringLiteral kind() const = 0; - // returns nullptr if the port doesn't exist + // If an error occurs, the ExpectedL will be in an error state. + // Otherwise, if the port is known, returns a pointer to RegistryEntry describing the port. + // Otherwise, returns a nullptr unique_ptr. virtual ExpectedL> get_port_entry(StringView port_name) const = 0; - // appends the names of the ports to the out parameter - // may result in duplicated port names; make sure to Util::sort_unique_erase at the end + // Appends the names of the known ports to the out parameter. + // May result in duplicated port names; make sure to Util::sort_unique_erase at the end virtual ExpectedL append_all_port_names(std::vector& port_names) const = 0; - // appends the names of the ports to the out parameter if this can be known without + // Appends the names of the ports to the out parameter if this can be known without // network access. - // returns true if names were appended, otherwise returns false. + // Returns true iff names were checked without network access. virtual ExpectedL try_append_all_port_names_no_network(std::vector& port_names) const = 0; - virtual ExpectedL get_baseline_version(StringView port_name) const = 0; + // If an error occurs, the ExpectedL will be in an error state. + // Otherwise, if the port is in the baseline, returns the version that baseline denotes. + // Otherwise, the Optional is disengaged. + virtual ExpectedL> get_baseline_version(StringView port_name) const = 0; virtual ~RegistryImplementation() = default; }; @@ -130,7 +135,7 @@ namespace vcpkg // the returned list is sorted by priority. std::vector registries_for_port(StringView name) const; - ExpectedL baseline_for_port(StringView port_name) const; + ExpectedL> baseline_for_port(StringView port_name) const; View registries() const { return registries_; } diff --git a/include/vcpkg/sourceparagraph.h b/include/vcpkg/sourceparagraph.h index fd2d0c3eae..bc849e0e95 100644 --- a/include/vcpkg/sourceparagraph.h +++ b/include/vcpkg/sourceparagraph.h @@ -214,11 +214,8 @@ namespace vcpkg std::string registry_location; }; - void print_error_message(Span> error_info_list); - inline void print_error_message(const std::unique_ptr& error_info_list) - { - return print_error_message({&error_info_list, 1}); - } + void print_error_message(const LocalizedString& message); + void print_error_message(const std::unique_ptr& error_info_list); std::string parse_spdx_license_expression(StringView sv, ParseMessages& messages); diff --git a/locales/messages.json b/locales/messages.json index a5d60e5f45..d402655f59 100644 --- a/locales/messages.json +++ b/locales/messages.json @@ -189,14 +189,10 @@ "BaselineConflict": "Specifying vcpkg-configuration.default-registry in a manifest file conflicts with built-in baseline.\nPlease remove one of these conflicting settings.", "BaselineFileNoDefaultField": "The baseline file at commit {commit_sha} was invalid (no \"default\" field).", "_BaselineFileNoDefaultField.comment": "An example of {commit_sha} is 7cfad47ae9f68b183983090afd6337cd60fd4949.", - "BaselineFileNoDefaultFieldPath": "baseline file at {path} was invalid (no \"default\" field)", - "_BaselineFileNoDefaultFieldPath.comment": "An example of {path} is /foo/bar.", "BaselineGitShowFailed": "while checking out baseline from commit '{commit_sha}', failed to `git show` versions/baseline.json. This may be fixed by fetching commits with `git fetch`.", "_BaselineGitShowFailed.comment": "An example of {commit_sha} is 7cfad47ae9f68b183983090afd6337cd60fd4949.", "BaselineMissing": "Baseline version not found. Run:\nvcpkg x-add-version {package_name}\ngit add versions\ngit commit -m \"Update version database\"\nto set {version} as the baseline version.", "_BaselineMissing.comment": "An example of {package_name} is zlib. An example of {version} is 1.3.8.", - "BaselineMissingDefault": "The baseline.json from commit `\"{commit_sha}\"` in the repo {url} was invalid (did not contain a \"default\" field).", - "_BaselineMissingDefault.comment": "An example of {commit_sha} is 7cfad47ae9f68b183983090afd6337cd60fd4949. An example of {url} is https://github.com/microsoft/vcpkg.", "BinaryCacheVendorHTTP": "HTTP servers", "BinarySourcesArg": "Binary caching sources. See 'vcpkg help binarycaching'", "_BinarySourcesArg.comment": "'vcpkg help binarycaching' is a command line and should not be localized", @@ -534,8 +530,6 @@ "CorruptedInstallTree": "Your vcpkg 'installed' tree is corrupted.", "CouldNotDeduceNugetIdAndVersion": "Could not deduce nuget id and version from filename: {path}", "_CouldNotDeduceNugetIdAndVersion.comment": "An example of {path} is /foo/bar.", - "CouldNotFindBaseline": "Could not find explicitly specified baseline `\"{commit_sha}\"` in baseline file {path}", - "_CouldNotFindBaseline.comment": "An example of {commit_sha} is 7cfad47ae9f68b183983090afd6337cd60fd4949. An example of {path} is /foo/bar.", "CouldNotFindBaselineInCommit": "Couldn't find baseline in {url} at {commit_sha} for {package_name}.", "_CouldNotFindBaselineInCommit.comment": "An example of {url} is https://github.com/microsoft/vcpkg. An example of {commit_sha} is 7cfad47ae9f68b183983090afd6337cd60fd4949. An example of {package_name} is zlib.", "CouldNotFindGitTreeAtCommit": "could not find the git tree for `versions` in repo {package_name} at commit {commit_sha}", @@ -742,7 +736,6 @@ "_FailedToExtract.comment": "An example of {path} is /foo/bar.", "FailedToFetchRepo": "Failed to fetch {url}.", "_FailedToFetchRepo.comment": "An example of {url} is https://github.com/microsoft/vcpkg.", - "FailedToFindBaseline": "Failed to find baseline.json", "FailedToFindPortFeature": "{package_name} has no feature named {feature}.", "_FailedToFindPortFeature.comment": "An example of {feature} is avisynthplus. An example of {package_name} is zlib.", "FailedToFormatMissingFile": "No files to format.\nPlease pass either --all, or the explicit files to format or convert.", diff --git a/src/vcpkg-test/registries.cpp b/src/vcpkg-test/registries.cpp index 9c81680f24..4ebc212e7c 100644 --- a/src/vcpkg-test/registries.cpp +++ b/src/vcpkg-test/registries.cpp @@ -28,7 +28,7 @@ namespace return !no_network_port_names.empty(); } - ExpectedL get_baseline_version(StringView) const override + ExpectedL> get_baseline_version(StringView) const override { return LocalizedString::from_raw("error"); } diff --git a/src/vcpkg/commands.add-version.cpp b/src/vcpkg/commands.add-version.cpp index dcf798faa4..4e5af3c8db 100644 --- a/src/vcpkg/commands.add-version.cpp +++ b/src/vcpkg/commands.add-version.cpp @@ -412,17 +412,17 @@ namespace vcpkg continue; } - auto maybe_scf = Paragraphs::try_load_port(fs, paths.builtin_ports_directory() / port_name); - if (!maybe_scf) + auto maybe_scf = + Paragraphs::try_load_port_required(fs, port_name, paths.builtin_ports_directory() / port_name); + auto scf = maybe_scf.get(); + if (!scf) { msg::println_error(msgAddVersionLoadPortFailed, msg::package_name = port_name); - print_error_message(maybe_scf.error()); + msg::println(Color::error, maybe_scf.error()); Checks::check_exit(VCPKG_LINE_INFO, !add_all); continue; } - const auto& scf = maybe_scf.value(VCPKG_LINE_INFO); - if (!skip_formatting_check) { // check if manifest file is property formatted @@ -430,7 +430,7 @@ namespace vcpkg if (fs.exists(path_to_manifest, IgnoreErrors{})) { const auto current_file_content = fs.read_contents(path_to_manifest, VCPKG_LINE_INFO); - const auto json = serialize_manifest(*scf); + const auto json = serialize_manifest(**scf); const auto formatted_content = Json::stringify(json); if (current_file_content != formatted_content) { @@ -454,7 +454,7 @@ namespace vcpkg msg::println_warning(msgAddVersionUncommittedChanges, msg::package_name = port_name); } - const auto& schemed_version = scf->to_schemed_version(); + const auto& schemed_version = (*scf)->to_schemed_version(); auto git_tree_it = git_tree_map.find(port_name); if (git_tree_it == git_tree_map.end()) diff --git a/src/vcpkg/commands.add.cpp b/src/vcpkg/commands.add.cpp index c3244a2d1e..0f20db198f 100644 --- a/src/vcpkg/commands.add.cpp +++ b/src/vcpkg/commands.add.cpp @@ -96,14 +96,15 @@ namespace vcpkg auto maybe_manifest_scf = SourceControlFile::parse_project_manifest_object(manifest->path, manifest->manifest, stdout_sink); - if (!maybe_manifest_scf) + auto pmanifest_scf = maybe_manifest_scf.get(); + if (!pmanifest_scf) { print_error_message(maybe_manifest_scf.error()); msg::println(Color::error, msg::msgSeeURL, msg::url = docs::manifests_url); Checks::exit_fail(VCPKG_LINE_INFO); } - auto& manifest_scf = *maybe_manifest_scf.value(VCPKG_LINE_INFO); + auto& manifest_scf = **pmanifest_scf; for (const auto& spec : specs) { auto dep = Util::find_if(manifest_scf.core_paragraph->dependencies, [&spec](Dependency& dep) { diff --git a/src/vcpkg/commands.autocomplete.cpp b/src/vcpkg/commands.autocomplete.cpp index 39321f0014..d43c755a25 100644 --- a/src/vcpkg/commands.autocomplete.cpp +++ b/src/vcpkg/commands.autocomplete.cpp @@ -104,8 +104,8 @@ namespace vcpkg StringView port_name{last_arg.begin(), colon}; StringView triplet_prefix{colon + 1, last_arg.end()}; // TODO: Support autocomplete for ports in --overlay-ports - auto maybe_port = - Paragraphs::try_load_port(paths.get_filesystem(), paths.builtin_ports_directory() / port_name); + auto maybe_port = Paragraphs::try_load_port_required( + paths.get_filesystem(), port_name, paths.builtin_ports_directory() / port_name); if (!maybe_port) { Checks::exit_success(VCPKG_LINE_INFO); diff --git a/src/vcpkg/commands.ci-verify-versions.cpp b/src/vcpkg/commands.ci-verify-versions.cpp index d26f592b1b..d067c95906 100644 --- a/src/vcpkg/commands.ci-verify-versions.cpp +++ b/src/vcpkg/commands.ci-verify-versions.cpp @@ -72,7 +72,8 @@ namespace const auto& file = maybe_file.value_or_exit(VCPKG_LINE_INFO); auto maybe_scf = Paragraphs::try_load_port_text(file, treeish, control_file == "vcpkg.json", stdout_sink); - if (!maybe_scf) + auto scf = maybe_scf.get(); + if (!scf) { return {msg::format_error(msgWhileParsingVersionsForPort, msg::package_name = port_name, @@ -82,12 +83,11 @@ namespace .append_raw('\n') .append(msgWhileLoadingPortFromGitTree, msg::commit_sha = treeish) .append_raw('\n') - .append_raw(maybe_scf.error()->error), + .append(maybe_scf.error()), expected_right_tag}; } - const auto& scf = maybe_scf.value(VCPKG_LINE_INFO); - auto&& git_tree_version = scf->to_schemed_version(); + auto&& git_tree_version = (*scf)->to_schemed_version(); if (version_entry.version.version != git_tree_version.version) { return { @@ -122,16 +122,17 @@ namespace } } - auto maybe_scf = Paragraphs::try_load_port(paths.get_filesystem(), port_path); - if (!maybe_scf) + auto maybe_scf = Paragraphs::try_load_port_required(paths.get_filesystem(), port_name, port_path); + auto scf = maybe_scf.get(); + if (!scf) { return {msg::format_error(msgWhileLoadingLocalPort, msg::package_name = port_name) .append_raw('\n') - .append_raw(maybe_scf.error()->error), + .append(maybe_scf.error()), expected_right_tag}; } - const auto local_port_version = maybe_scf.value(VCPKG_LINE_INFO)->to_schemed_version(); + const auto local_port_version = (*scf)->to_schemed_version(); auto versions_end = versions->end(); auto it = std::find_if(versions->begin(), versions_end, [&](const GitVersionDbEntry& entry) { diff --git a/src/vcpkg/commands.format-manifest.cpp b/src/vcpkg/commands.format-manifest.cpp index 3e5659f828..f4d04a031a 100644 --- a/src/vcpkg/commands.format-manifest.cpp +++ b/src/vcpkg/commands.format-manifest.cpp @@ -50,7 +50,6 @@ namespace { msg::println_error(msgFailedToParseManifest, msg::path = path_string); print_error_message(scf.error()); - msg::println(); return nullopt; } diff --git a/src/vcpkg/paragraphs.cpp b/src/vcpkg/paragraphs.cpp index 8166b9adc3..9b947cafde 100644 --- a/src/vcpkg/paragraphs.cpp +++ b/src/vcpkg/paragraphs.cpp @@ -13,6 +13,8 @@ #include #include +#include + static std::atomic g_load_ports_stats(0); namespace vcpkg @@ -79,6 +81,11 @@ namespace vcpkg return result; } + LocalizedString ToLocalizedString_t::operator()(std::unique_ptr p) const + { + return LocalizedString::from_raw(p->to_string()); + } + std::unique_ptr ParseControlErrorInfo::from_error(StringView name, LocalizedString&& ls) { auto error_info = std::make_unique(); @@ -373,9 +380,9 @@ namespace vcpkg::Paragraphs fs.exists(maybe_directory / "vcpkg.json", IgnoreErrors{}); } - static ParseExpected try_load_manifest_text(const std::string& text, - StringView origin, - MessageSink& warning_sink) + static ExpectedL> try_load_manifest_text(const std::string& text, + StringView origin, + MessageSink& warning_sink) { auto res = Json::parse(text, origin); if (auto val = res.get()) @@ -383,19 +390,20 @@ namespace vcpkg::Paragraphs if (val->value.is_object()) { return SourceControlFile::parse_port_manifest_object( - origin, val->value.object(VCPKG_LINE_INFO), warning_sink); + origin, val->value.object(VCPKG_LINE_INFO), warning_sink) + .map_error(ToLocalizedString); } - return ParseControlErrorInfo::from_error(origin, msg::format(msgJsonValueNotObject)); + return msg::format(msgJsonValueNotObject); } - return ParseControlErrorInfo::from_error(origin, LocalizedString::from_raw(res.error()->to_string())); + return LocalizedString::from_raw(res.error()->to_string()); } - ParseExpected try_load_port_text(const std::string& text, - StringView origin, - bool is_manifest, - MessageSink& warning_sink) + ExpectedL> try_load_port_text(const std::string& text, + StringView origin, + bool is_manifest, + MessageSink& warning_sink) { StatsTimer timer(g_load_ports_stats); @@ -404,22 +412,23 @@ namespace vcpkg::Paragraphs return try_load_manifest_text(text, origin, warning_sink); } - ExpectedL> pghs = parse_paragraphs(StringView{text}, origin); + auto pghs = parse_paragraphs(StringView{text}, origin); if (auto vector_pghs = pghs.get()) { - return SourceControlFile::parse_control_file(origin, std::move(*vector_pghs)); + return SourceControlFile::parse_control_file(origin, std::move(*vector_pghs)).map_error(ToLocalizedString); } - return ParseControlErrorInfo::from_error(origin, std::move(pghs).error()); + return std::move(pghs).error(); } - ParseExpected try_load_port(const ReadOnlyFilesystem& fs, const Path& port_directory) + ExpectedL> try_load_port(const ReadOnlyFilesystem& fs, + StringView port_name, + const Path& port_directory) { StatsTimer timer(g_load_ports_stats); const auto manifest_path = port_directory / "vcpkg.json"; const auto control_path = port_directory / "CONTROL"; - const auto port_name = port_directory.filename(); std::error_code ec; auto manifest_contents = fs.read_contents(manifest_path, ec); if (ec) @@ -427,45 +436,51 @@ namespace vcpkg::Paragraphs const auto exists = ec != std::errc::no_such_file_or_directory; if (exists) { - auto formatted = msg::format_error(msgFailedToParseManifest, msg::path = manifest_path) - .append_raw("\n") - .append(format_filesystem_call_error(ec, "read_contents", {manifest_path})); - - return ParseControlErrorInfo::from_error(port_name, std::move(formatted)); + return msg::format_error(msgFailedToParseManifest, msg::path = manifest_path) + .append_raw("\n") + .append(format_filesystem_call_error(ec, "read_contents", {manifest_path})); } if (fs.exists(control_path, IgnoreErrors{})) { - ExpectedL> pghs = get_paragraphs(fs, control_path); - if (auto vector_pghs = pghs.get()) - { - return SourceControlFile::parse_control_file(control_path, std::move(*vector_pghs)); - } - - return ParseControlErrorInfo::from_error(port_name, std::move(pghs).error()); + return get_paragraphs(fs, control_path).then([&](std::vector&& vector_pghs) { + return SourceControlFile::parse_control_file(control_path, std::move(vector_pghs)) + .map_error(ToLocalizedString); + }); } if (fs.exists(port_directory, IgnoreErrors{})) { - return ParseControlErrorInfo::from_error(port_name, - msg::format_error(msgPortMissingManifest, - msg::package_name = port_name, - msg::path = port_directory)); + return msg::format_error( + msgPortMissingManifest, msg::package_name = port_name, msg::path = port_directory); } - return ParseControlErrorInfo::from_error( - port_name, msg::format_error(msgPortDoesNotExist, msg::package_name = port_name)); + return std::unique_ptr(); } if (fs.exists(control_path, IgnoreErrors{})) { - return ParseControlErrorInfo::from_error( - port_name, msg::format_error(msgManifestConflict, msg::path = port_directory)); + return msg::format_error(msgManifestConflict, msg::path = port_directory); } return try_load_manifest_text(manifest_contents, manifest_path, stdout_sink); } + ExpectedL> try_load_port_required(const ReadOnlyFilesystem& fs, + StringView port_name, + const Path& port_directory) + { + return try_load_port(fs, port_name, port_directory) + .then([&](std::unique_ptr&& loaded) -> ExpectedL> { + if (!loaded) + { + return msg::format_error(msgPortDoesNotExist, msg::package_name = port_name); + } + + return std::move(loaded); + }); + } + ExpectedL try_load_cached_package(const ReadOnlyFilesystem& fs, const Path& package_dir, const PackageSpec& spec) @@ -513,7 +528,7 @@ namespace vcpkg::Paragraphs continue; } - auto maybe_baseline_version = impl->get_baseline_version(port_name); + auto maybe_baseline_version = impl->get_baseline_version(port_name).value_or_exit(VCPKG_LINE_INFO); auto baseline_version = maybe_baseline_version.get(); if (!baseline_version) continue; // port is attributed to this registry, but it is not in the baseline auto maybe_port_entry = impl->get_port_entry(port_name); @@ -523,7 +538,7 @@ namespace vcpkg::Paragraphs auto maybe_port_location = (*port_entry)->get_version(*baseline_version); const auto port_location = maybe_port_location.get(); if (!port_location) continue; // baseline version was not in version db (registry consistency issue) - auto maybe_spgh = try_load_port(fs, port_location->path); + auto maybe_spgh = try_load_port_required(fs, port_name, port_location->path); if (const auto spgh = maybe_spgh.get()) { ret.paragraphs.push_back({ @@ -534,7 +549,9 @@ namespace vcpkg::Paragraphs } else { - ret.errors.emplace_back(std::move(maybe_spgh).error()); + ret.errors.emplace_back(std::piecewise_construct, + std::forward_as_tuple(port_name.data(), port_name.size()), + std::forward_as_tuple(std::move(maybe_spgh).error())); } } @@ -547,14 +564,20 @@ namespace vcpkg::Paragraphs { if (Debug::g_debugging) { - print_error_message(results.errors); + print_error_message(LocalizedString::from_raw( + Strings::join("\n", + results.errors, + [](const std::pair& err) -> const LocalizedString& { + return err.second; + }))); } else { for (auto&& error : results.errors) { - msg::println_warning(msgErrorWhileParsing, msg::path = error->name); + msg::println_warning(msgErrorWhileParsing, msg::path = error.first); } + msg::println_warning(msgGetParseFailureInfo); } } @@ -580,14 +603,17 @@ namespace vcpkg::Paragraphs for (auto&& path : port_dirs) { - auto maybe_spgh = try_load_port(fs, path); + auto port_name = path.filename(); + auto maybe_spgh = try_load_port_required(fs, port_name, path); if (const auto spgh = maybe_spgh.get()) { ret.paragraphs.push_back({std::move(*spgh), std::move(path)}); } else { - ret.errors.emplace_back(std::move(maybe_spgh).error()); + ret.errors.emplace_back(std::piecewise_construct, + std::forward_as_tuple(port_name.data(), port_name.size()), + std::forward_as_tuple(std::move(maybe_spgh).error())); } } diff --git a/src/vcpkg/portfileprovider.cpp b/src/vcpkg/portfileprovider.cpp index 410d4cc301..5eb0174f7e 100644 --- a/src/vcpkg/portfileprovider.cpp +++ b/src/vcpkg/portfileprovider.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -108,22 +109,27 @@ namespace vcpkg virtual ExpectedL get_baseline_version(StringView port_name) const override { - auto it = m_baseline_cache.find(port_name); - if (it != m_baseline_cache.end()) - { - return it->second; - } - else - { - auto version = registry_set.baseline_for_port(port_name); - m_baseline_cache.emplace(port_name.to_string(), version); - return version; - } + return m_baseline_cache.get_lazy(port_name, [this, port_name]() -> ExpectedL { + auto maybe_maybe_version = registry_set.baseline_for_port(port_name); + auto maybe_version = maybe_maybe_version.get(); + if (!maybe_version) + { + return std::move(maybe_maybe_version).error(); + } + + auto version = maybe_version->get(); + if (!version) + { + return msg::format_error(msgPortNotInBaseline, msg::package_name = port_name); + } + + return std::move(*version); + }); } private: const RegistrySet& registry_set; - mutable std::map, std::less<>> m_baseline_cache; + Cache> m_baseline_cache; }; struct VersionedPortfileProviderImpl : IFullVersionedPortfileProvider @@ -182,7 +188,8 @@ namespace vcpkg auto maybe_path = ent->get()->get_version(version_spec.version); if (auto path = maybe_path.get()) { - auto maybe_control_file = Paragraphs::try_load_port(m_fs, path->path); + auto maybe_control_file = + Paragraphs::try_load_port_required(m_fs, version_spec.port_name, path->path); if (auto scf = maybe_control_file.get()) { auto scf_vspec = scf->get()->to_version_spec(); @@ -289,7 +296,7 @@ namespace vcpkg // Try loading individual port if (Paragraphs::is_port_directory(m_fs, ports_dir)) { - auto maybe_scf = Paragraphs::try_load_port(m_fs, ports_dir); + auto maybe_scf = Paragraphs::try_load_port_required(m_fs, port_name, ports_dir); if (auto scfp = maybe_scf.get()) { auto& scf = *scfp; @@ -313,7 +320,7 @@ namespace vcpkg auto ports_spec = ports_dir / port_name; if (Paragraphs::is_port_directory(m_fs, ports_spec)) { - auto found_scf = Paragraphs::try_load_port(m_fs, ports_spec); + auto found_scf = Paragraphs::try_load_port_required(m_fs, port_name, ports_spec); if (auto scfp = found_scf.get()) { auto& scf = *scfp; @@ -321,6 +328,7 @@ namespace vcpkg { return SourceControlFileAndLocation{std::move(scf), std::move(ports_spec)}; } + Checks::msg_exit_maybe_upgrade( VCPKG_LINE_INFO, msg::format(msgFailedToLoadPort, msg::package_name = port_name, msg::path = ports_spec) @@ -363,7 +371,7 @@ namespace vcpkg // Try loading individual port if (Paragraphs::is_port_directory(m_fs, ports_dir)) { - auto maybe_scf = Paragraphs::try_load_port(m_fs, ports_dir); + auto maybe_scf = Paragraphs::try_load_port_required(m_fs, ports_dir.filename(), ports_dir); if (auto scfp = maybe_scf.get()) { SourceControlFileAndLocation scfl{std::move(*scfp), ports_dir}; diff --git a/src/vcpkg/registries.cpp b/src/vcpkg/registries.cpp index af73570ba3..f7919a39f8 100644 --- a/src/vcpkg/registries.cpp +++ b/src/vcpkg/registries.cpp @@ -284,7 +284,7 @@ namespace ExpectedL try_append_all_port_names_no_network(std::vector& port_names) const override; - ExpectedL get_baseline_version(StringView) const override; + ExpectedL> get_baseline_version(StringView) const override; private: friend struct GitRegistryEntry; @@ -481,23 +481,21 @@ namespace ExpectedL try_append_all_port_names_no_network(std::vector& port_names) const override; - ExpectedL get_baseline_version(StringView port_name) const override; + ExpectedL> get_baseline_version(StringView port_name) const override; ~BuiltinFilesRegistry() = default; DelayedInit m_baseline; private: - const ExpectedL& get_scf(const Path& path) const + const ExpectedL>& get_scf(StringView port_name, const Path& path) const { - return m_scfs.get_lazy(path, [this, &path]() { - return map_parse_expected_to_localized_string(Paragraphs::try_load_port(m_fs, path)); - }); + return m_scfs.get_lazy(path, [&, this]() { return Paragraphs::try_load_port(m_fs, port_name, path); }); } const ReadOnlyFilesystem& m_fs; const Path m_builtin_ports_directory; - Cache> m_scfs; + Cache>> m_scfs; }; constexpr StringLiteral BuiltinFilesRegistry::s_kind; @@ -522,12 +520,12 @@ namespace ExpectedL try_append_all_port_names_no_network(std::vector& port_names) const override; - ExpectedL get_baseline_version(StringView port_name) const override; + ExpectedL> get_baseline_version(StringView port_name) const override; ~BuiltinGitRegistry() = default; std::string m_baseline_identifier; - DelayedInit m_baseline; + DelayedInit> m_baseline; private: std::unique_ptr m_files_impl; @@ -559,7 +557,7 @@ namespace return msg::format_error(msgErrorRequireBaseline); } - ExpectedL get_baseline_version(StringView) const override + ExpectedL> get_baseline_version(StringView) const override { return msg::format_error(msgErrorRequireBaseline); } @@ -583,14 +581,14 @@ namespace ExpectedL try_append_all_port_names_no_network(std::vector& port_names) const override; - ExpectedL get_baseline_version(StringView) const override; + ExpectedL> get_baseline_version(StringView) const override; private: const ReadOnlyFilesystem& m_fs; Path m_path; std::string m_baseline_identifier; - DelayedInit m_baseline; + DelayedInit> m_baseline; }; Path relative_path_to_versions(StringView port_name); @@ -603,10 +601,10 @@ namespace // returns nullopt if the baseline is valid, but doesn't contain the specified baseline, // or (equivalently) if the baseline does not exist. - ExpectedL> parse_baseline_versions(StringView contents, StringView baseline, StringView origin); - ExpectedL> load_baseline_versions(const ReadOnlyFilesystem& fs, - const Path& baseline_path, - StringView identifier = {}); + ExpectedL parse_baseline_versions(StringView contents, StringView baseline, StringView origin); + ExpectedL load_baseline_versions(const ReadOnlyFilesystem& fs, + const Path& baseline_path, + StringView identifier = {}); ExpectedL load_all_port_names_from_registry_versions(std::vector& out, const ReadOnlyFilesystem& fs, @@ -713,40 +711,48 @@ namespace ExpectedL> BuiltinFilesRegistry::get_port_entry(StringView port_name) const { auto port_directory = m_builtin_ports_directory / port_name; - if (m_fs.exists(port_directory, IgnoreErrors{})) + const auto& maybe_maybe_scf = get_scf(port_name, port_directory); + const auto maybe_scf = maybe_maybe_scf.get(); + if (!maybe_scf) { - const auto& found_scf = get_scf(port_directory); - if (auto scf = found_scf.get()) - { - if (scf->core_paragraph->name == port_name) - { - return std::make_unique( - scf->core_paragraph->name, port_directory, scf->to_version()); - } + return maybe_maybe_scf.error(); + } - return msg::format_error(msgUnexpectedPortName, - msg::expected = scf->core_paragraph->name, - msg::actual = port_name, - msg::path = port_directory); - } + auto scf = maybe_scf->get(); + if (!scf) + { + return std::unique_ptr(); + } - return found_scf.error(); + if (scf->core_paragraph->name == port_name) + { + return std::make_unique( + scf->core_paragraph->name, port_directory, scf->to_version()); } - return nullptr; + return msg::format_error(msgUnexpectedPortName, + msg::expected = scf->core_paragraph->name, + msg::actual = port_name, + msg::path = port_directory); } - ExpectedL BuiltinFilesRegistry::get_baseline_version(StringView port_name) const + ExpectedL> BuiltinFilesRegistry::get_baseline_version(StringView port_name) const { // if a baseline is not specified, use the ports directory version - auto port_path = m_builtin_ports_directory / port_name; - const auto& maybe_scf = get_scf(port_path); - if (auto pscf = maybe_scf.get()) + const auto& maybe_maybe_scf = get_scf(port_name, m_builtin_ports_directory / port_name); + auto maybe_scf = maybe_maybe_scf.get(); + if (!maybe_scf) + { + return maybe_maybe_scf.error(); + } + + auto scf = maybe_scf->get(); + if (!scf) { - return pscf->to_version(); + return Optional(); } - return maybe_scf.error(); + return scf->to_version(); } ExpectedL BuiltinFilesRegistry::append_all_port_names(std::vector& out) const @@ -799,31 +805,30 @@ namespace return res; } - ExpectedL BuiltinGitRegistry::get_baseline_version(StringView port_name) const + ExpectedL> BuiltinGitRegistry::get_baseline_version(StringView port_name) const { - const auto& baseline = m_baseline.get([this]() -> Baseline { - auto maybe_path = git_checkout_baseline(m_paths, m_baseline_identifier); - if (!maybe_path) - { - msg::println(Color::error, LocalizedString::from_raw(maybe_path.error())); - msg::println(Color::error, LocalizedString::from_raw(m_paths.get_current_git_sha_baseline_message())); - Checks::exit_fail(VCPKG_LINE_INFO); - } - auto b = load_baseline_versions(m_paths.get_filesystem(), *maybe_path.get()).value_or_exit(VCPKG_LINE_INFO); - if (auto p = b.get()) - { - return std::move(*p); - } - Checks::msg_exit_with_message( - VCPKG_LINE_INFO, msgBaselineFileNoDefaultField, msg::commit_sha = m_baseline_identifier); + const auto& maybe_baseline = m_baseline.get([this]() -> ExpectedL { + return git_checkout_baseline(m_paths, m_baseline_identifier) + .then([&](Path&& path) { return load_baseline_versions(m_paths.get_filesystem(), path); }) + .map_error([&](LocalizedString&& error) { + return std::move(error).append(msgWhileCheckingOutBaseline, + msg::commit_sha = m_baseline_identifier); + }); }); - auto it = baseline.find(port_name); - if (it != baseline.end()) + auto baseline = maybe_baseline.get(); + if (!baseline) + { + return maybe_baseline.error(); + } + + auto it = baseline->find(port_name); + if (it != baseline->end()) { return it->second; } - return msg::format(msg::msgErrorMessage).append(msgPortNotInBaseline, msg::package_name = port_name); + + return Optional(); } ExpectedL BuiltinGitRegistry::append_all_port_names(std::vector& out) const @@ -845,41 +850,22 @@ namespace // } BuiltinGitRegistry::RegistryImplementation // { FilesystemRegistry::RegistryImplementation - ExpectedL FilesystemRegistry::get_baseline_version(StringView port_name) const - { - const auto& baseline = m_baseline.get([this]() -> Baseline { - auto path_to_baseline = m_path / registry_versions_dir_name / "baseline.json"; - auto res_baseline = load_baseline_versions(m_fs, path_to_baseline, m_baseline_identifier); - if (auto opt_baseline = res_baseline.get()) - { - if (auto p = opt_baseline->get()) - { - return std::move(*p); - } - - if (m_baseline_identifier.size() == 0) + ExpectedL> FilesystemRegistry::get_baseline_version(StringView port_name) const + { + return m_baseline + .get([this]() { + return load_baseline_versions( + m_fs, m_path / registry_versions_dir_name / "baseline.json", m_baseline_identifier); + }) + .then([&](const Baseline& baseline) -> ExpectedL> { + auto it = baseline.find(port_name); + if (it != baseline.end()) { - return {}; + return it->second; } - Checks::msg_exit_with_error(VCPKG_LINE_INFO, - msgCouldNotFindBaseline, - msg::commit_sha = m_baseline_identifier, - msg::path = path_to_baseline); - } - - Checks::msg_exit_maybe_upgrade(VCPKG_LINE_INFO, res_baseline.error()); - }); - - auto it = baseline.find(port_name); - if (it != baseline.end()) - { - return it->second; - } - else - { - return msg::format(msg::msgErrorMessage).append(msgPortNotInBaseline, msg::package_name = port_name); - } + return Optional(); + }); } ExpectedL> FilesystemRegistry::get_port_entry(StringView port_name) const @@ -990,109 +976,100 @@ namespace { } - ExpectedL GitRegistry::get_baseline_version(StringView port_name) const - { - const auto& baseline = - m_baseline - .get([this, port_name]() -> ExpectedL { - // We delay baseline validation until here to give better error messages and suggestions - if (!is_git_commit_sha(m_baseline_identifier)) - { - auto& maybe_lock_entry = get_lock_entry(); - auto lock_entry = maybe_lock_entry.get(); - if (!lock_entry) - { - return maybe_lock_entry.error(); - } - - auto maybe_up_to_date = lock_entry->ensure_up_to_date(m_paths); - if (maybe_up_to_date) - { - return msg::format_error(msgGitRegistryMustHaveBaseline, - msg::url = m_repo, - msg::commit_sha = lock_entry->commit_id()); - } - - return std::move(maybe_up_to_date).error(); - } - - auto path_to_baseline = Path(registry_versions_dir_name.to_string()) / "baseline.json"; - auto maybe_contents = - m_paths.git_show_from_remote_registry(m_baseline_identifier, path_to_baseline); - if (!maybe_contents) - { - auto& maybe_lock_entry = get_lock_entry(); - auto lock_entry = maybe_lock_entry.get(); - if (!lock_entry) - { - return maybe_lock_entry.error(); - } - - auto maybe_up_to_date = lock_entry->ensure_up_to_date(m_paths); - if (!maybe_up_to_date) - { - return std::move(maybe_up_to_date).error(); - } - - maybe_contents = m_paths.git_show_from_remote_registry(m_baseline_identifier, path_to_baseline); - } - - if (!maybe_contents) - { - msg::println(msgFetchingBaselineInfo, msg::package_name = m_repo); - auto maybe_err = m_paths.git_fetch(m_repo, m_baseline_identifier); - if (!maybe_err) - { - get_global_metrics_collector().track_define( - DefineMetric::RegistriesErrorCouldNotFindBaseline); - return msg::format_error(msgFailedToFetchRepo, msg::url = m_repo) - .append_raw('\n') - .append(maybe_err.error()); - } - - maybe_contents = m_paths.git_show_from_remote_registry(m_baseline_identifier, path_to_baseline); - } - - if (!maybe_contents) - { - get_global_metrics_collector().track_define(DefineMetric::RegistriesErrorCouldNotFindBaseline); - return msg::format_error(msgCouldNotFindBaselineInCommit, - msg::url = m_repo, - msg::commit_sha = m_baseline_identifier, - msg::package_name = port_name) - .append_raw('\n') - .append_raw(maybe_contents.error()); - } - - auto contents = maybe_contents.get(); - auto res_baseline = parse_baseline_versions(*contents, "default", path_to_baseline); - if (auto opt_baseline = res_baseline.get()) - { - if (auto p = opt_baseline->get()) - { - return std::move(*p); - } - - get_global_metrics_collector().track_define(DefineMetric::RegistriesErrorCouldNotFindBaseline); - return msg::format_error( - msgBaselineMissingDefault, msg::commit_sha = m_baseline_identifier, msg::url = m_repo); - } + ExpectedL> GitRegistry::get_baseline_version(StringView port_name) const + { + const auto& maybe_baseline = m_baseline.get([this, port_name]() -> ExpectedL { + // We delay baseline validation until here to give better error messages and suggestions + if (!is_git_commit_sha(m_baseline_identifier)) + { + auto& maybe_lock_entry = get_lock_entry(); + auto lock_entry = maybe_lock_entry.get(); + if (!lock_entry) + { + return maybe_lock_entry.error(); + } + auto maybe_up_to_date = lock_entry->ensure_up_to_date(m_paths); + if (maybe_up_to_date) + { + return msg::format_error( + msgGitRegistryMustHaveBaseline, msg::url = m_repo, msg::commit_sha = lock_entry->commit_id()); + } + + return std::move(maybe_up_to_date).error(); + } + + auto path_to_baseline = Path(registry_versions_dir_name.to_string()) / "baseline.json"; + auto maybe_contents = m_paths.git_show_from_remote_registry(m_baseline_identifier, path_to_baseline); + if (!maybe_contents) + { + auto& maybe_lock_entry = get_lock_entry(); + auto lock_entry = maybe_lock_entry.get(); + if (!lock_entry) + { + return maybe_lock_entry.error(); + } + + auto maybe_up_to_date = lock_entry->ensure_up_to_date(m_paths); + if (!maybe_up_to_date) + { + return std::move(maybe_up_to_date).error(); + } + + maybe_contents = m_paths.git_show_from_remote_registry(m_baseline_identifier, path_to_baseline); + } + + if (!maybe_contents) + { + msg::println(msgFetchingBaselineInfo, msg::package_name = m_repo); + auto maybe_err = m_paths.git_fetch(m_repo, m_baseline_identifier); + if (!maybe_err) + { + get_global_metrics_collector().track_define(DefineMetric::RegistriesErrorCouldNotFindBaseline); + return msg::format_error(msgFailedToFetchRepo, msg::url = m_repo) + .append_raw('\n') + .append(maybe_err.error()); + } + + maybe_contents = m_paths.git_show_from_remote_registry(m_baseline_identifier, path_to_baseline); + } + + if (!maybe_contents) + { + get_global_metrics_collector().track_define(DefineMetric::RegistriesErrorCouldNotFindBaseline); + return msg::format_error(msgCouldNotFindBaselineInCommit, + msg::url = m_repo, + msg::commit_sha = m_baseline_identifier, + msg::package_name = port_name) + .append_raw('\n') + .append_raw(maybe_contents.error()); + } + + auto contents = maybe_contents.get(); + return parse_baseline_versions(*contents, "default", path_to_baseline) + .map_error([&](LocalizedString&& error) { + get_global_metrics_collector().track_define(DefineMetric::RegistriesErrorCouldNotFindBaseline); return msg::format_error(msgErrorWhileFetchingBaseline, msg::value = m_baseline_identifier, msg::package_name = m_repo) .append_raw('\n') - .append(res_baseline.error()); - }) - .value_or_exit(VCPKG_LINE_INFO); + .append(error); + }); + }); - auto it = baseline.find(port_name); - if (it != baseline.end()) + auto baseline = maybe_baseline.get(); + if (!baseline) + { + return maybe_baseline.error(); + } + + auto it = baseline->find(port_name); + if (it != baseline->end()) { return it->second; } - return msg::format(msg::msgErrorMessage).append(msgPortNotInBaseline, msg::package_name = port_name); + return Optional(); } ExpectedL GitRegistry::append_all_port_names(std::vector& out) const @@ -1385,7 +1362,7 @@ namespace return db_entries; } - ExpectedL> parse_baseline_versions(StringView contents, StringView baseline, StringView origin) + ExpectedL parse_baseline_versions(StringView contents, StringView baseline, StringView origin) { auto maybe_value = Json::parse(contents, origin); if (!maybe_value) @@ -1400,20 +1377,24 @@ namespace } auto real_baseline = baseline.size() == 0 ? "default" : baseline; - const auto& obj = value.value.object(VCPKG_LINE_INFO); auto baseline_value = obj.get(real_baseline); if (!baseline_value) { - return {nullopt, expected_left_tag}; + return LocalizedString::from_raw(origin) + .append_raw(": ") + .append(msgErrorMessage) + .append(msgMissingRequiredField, + msg::json_field = baseline, + msg::json_type = msg::format(msgABaselineObject)); } Json::Reader r; - std::map> result; + Baseline result; r.visit_in_key(*baseline_value, real_baseline, result, BaselineDeserializer::instance); if (r.errors().empty()) { - return {std::move(result), expected_left_tag}; + return std::move(result); } else { @@ -1423,24 +1404,13 @@ namespace } } - ExpectedL> load_baseline_versions(const ReadOnlyFilesystem& fs, - const Path& baseline_path, - StringView baseline) + ExpectedL load_baseline_versions(const ReadOnlyFilesystem& fs, + const Path& baseline_path, + StringView baseline) { - std::error_code ec; - auto contents = fs.read_contents(baseline_path, ec); - if (ec) - { - if (ec == std::errc::no_such_file_or_directory) - { - msg::println(msgFailedToFindBaseline); - return {nullopt, expected_left_tag}; - } - - return format_filesystem_call_error(ec, "read_contents", {baseline_path}); - } - - return parse_baseline_versions(contents, baseline, baseline_path); + return fs.try_read_contents(baseline_path).then([&](FileContents&& fc) { + return parse_baseline_versions(fc.content, baseline, fc.origin); + }); } } @@ -1569,7 +1539,7 @@ namespace vcpkg return Util::fmap(std::move(candidates), [](const RegistryCandidate& target) { return target.impl; }); } - ExpectedL RegistrySet::baseline_for_port(StringView port_name) const + ExpectedL> RegistrySet::baseline_for_port(StringView port_name) const { auto impl = registry_for_port(port_name); if (!impl) return msg::format(msg::msgErrorMessage).append(msgNoRegistryForPort, msg::package_name = port_name); @@ -1684,16 +1654,7 @@ namespace vcpkg ExpectedL get_builtin_baseline(const VcpkgPaths& paths) { - auto baseline_path = paths.builtin_registry_versions / "baseline.json"; - return load_baseline_versions(paths.get_filesystem(), baseline_path) - .then([&](Optional&& b) -> ExpectedL { - if (auto p = b.get()) - { - return std::move(*p); - } - - return msg::format_error(msgBaselineFileNoDefaultFieldPath, msg::path = baseline_path); - }); + return load_baseline_versions(paths.get_filesystem(), paths.builtin_registry_versions / "baseline.json"); } bool is_git_commit_sha(StringView sv) diff --git a/src/vcpkg/sourceparagraph.cpp b/src/vcpkg/sourceparagraph.cpp index 3cab387036..3ecf6c4a88 100644 --- a/src/vcpkg/sourceparagraph.cpp +++ b/src/vcpkg/sourceparagraph.cpp @@ -185,8 +185,6 @@ namespace vcpkg return valid_fields; } - void print_error_message(Span> error_info_list); - static void trim_all(std::vector& arr) { for (auto& el : arr) @@ -1521,18 +1519,16 @@ namespace vcpkg return Strings::starts_with(sv, "Error") || Strings::starts_with(sv, "error: "); } - void print_error_message(Span> error_info_list) + void print_error_message(const LocalizedString& message) { - auto msg = ParseControlErrorInfo::format_errors(error_info_list); - // To preserve previous behavior, each line starting with "Error" should be error-colored. All other lines // should be neutral color. // To minimize the number of print calls on Windows (which is a significant performance bottleneck), this // algorithm chunks groups of similarly-colored lines. - const char* start_of_chunk = msg.data(); - const char* end_of_chunk = msg.data(); - const char* const last = msg.data() + msg.size(); + const char* start_of_chunk = message.data().data(); + const char* end_of_chunk = start_of_chunk; + const char* const last = start_of_chunk + message.data().size(); while (end_of_chunk != last) { while (end_of_chunk != last && starts_with_error({end_of_chunk, last})) @@ -1555,6 +1551,14 @@ namespace vcpkg start_of_chunk = end_of_chunk; } } + + msg::println(); + } + + void print_error_message(const std::unique_ptr& error_info_list) + { + print_error_message(LocalizedString::from_raw( + ParseControlErrorInfo::format_errors(View>{&error_info_list, 1}))); } Optional SourceControlFile::find_feature(StringView featurename) const