From 607c207d590d1783860d22c1fe7c80ea813a4738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?MACHIZAUD=20Andr=C3=A9a?= Date: Wed, 13 Apr 2022 17:36:59 +0200 Subject: [PATCH 1/3] Mark location where consistent registry resolution/access is required --- src/vcpkg/commands.add-version.cpp | 8 ++++++++ src/vcpkg/commands.format-manifest.cpp | 2 ++ 2 files changed, 10 insertions(+) diff --git a/src/vcpkg/commands.add-version.cpp b/src/vcpkg/commands.add-version.cpp index ca48a1b2ff..b3def68905 100644 --- a/src/vcpkg/commands.add-version.cpp +++ b/src/vcpkg/commands.add-version.cpp @@ -431,6 +431,7 @@ namespace vcpkg::Commands::AddVersion const bool verbose = !add_all || Util::Sets::contains(parsed_args.switches, OPTION_VERBOSE); auto& fs = paths.get_filesystem(); + // TODO: Handle consistent port access auto baseline_path = paths.builtin_registry_versions / "baseline.json"; if (!fs.exists(baseline_path, IgnoreErrors{})) { @@ -454,6 +455,7 @@ namespace vcpkg::Commands::AddVersion msg::command_name = "x-add-version", msg::option = OPTION_ALL); + // TODO: Handle consistent port access for (auto&& port_dir : fs.get_directories_non_recursive(paths.builtin_ports_directory(), VCPKG_LINE_INFO)) { port_names.emplace_back(port_dir.stem().to_string()); @@ -466,6 +468,7 @@ namespace vcpkg::Commands::AddVersion std::map> ret; return ret; } + // TODO: Handle consistent port access auto maybe_baseline_map = vcpkg::get_builtin_baseline(paths); return maybe_baseline_map.value_or_exit(VCPKG_LINE_INFO); }(); @@ -489,6 +492,7 @@ namespace vcpkg::Commands::AddVersion for (auto&& port_name : port_names) { + // TODO: Handle consistent port access auto port_dir = paths.builtin_ports_directory() / port_name; if (!fs.exists(port_dir, IgnoreErrors{})) @@ -498,6 +502,7 @@ namespace vcpkg::Commands::AddVersion continue; } + // TODO: Handle consistent port access auto maybe_scf = Paragraphs::try_load_port(fs, paths.builtin_ports_directory() / port_name); if (!maybe_scf.has_value()) { @@ -512,6 +517,7 @@ namespace vcpkg::Commands::AddVersion if (!skip_formatting_check) { // check if manifest file is property formatted + // TODO: Handle consistent port access const auto path_to_manifest = paths.builtin_ports_directory() / port_name / "vcpkg.json"; if (fs.exists(path_to_manifest, IgnoreErrors{})) { @@ -559,6 +565,7 @@ namespace vcpkg::Commands::AddVersion const auto& git_tree = git_tree_it->second; char prefix[] = {port_name[0], '-', '\0'}; + // TODO: Handle consistent port access auto port_versions_path = paths.builtin_registry_versions / prefix / Strings::concat(port_name, ".json"); auto updated_versions_file = update_version_db_file(paths, port_name, @@ -569,6 +576,7 @@ namespace vcpkg::Commands::AddVersion verbose, add_all, skip_version_format_check); + // TODO: Handle consistent port access auto updated_baseline_file = update_baseline_version( paths, port_name, schemed_version.version, baseline_path, baseline_map, verbose); if (verbose && updated_versions_file == UpdateResult::NotUpdated && diff --git a/src/vcpkg/commands.format-manifest.cpp b/src/vcpkg/commands.format-manifest.cpp index 76f62c0fcd..b8311cbb79 100644 --- a/src/vcpkg/commands.format-manifest.cpp +++ b/src/vcpkg/commands.format-manifest.cpp @@ -222,6 +222,7 @@ namespace vcpkg::Commands::FormatManifest { if (path.is_relative()) { + // TODO: Handle consistent port access path = paths.original_cwd / path; } @@ -237,6 +238,7 @@ namespace vcpkg::Commands::FormatManifest if (format_all) { + // TODO: Handle consistent port access for (const auto& dir : fs.get_directories_non_recursive(paths.builtin_ports_directory(), VCPKG_LINE_INFO)) { auto control_path = dir / "CONTROL"; From 2cb7e086281dd8c38c9e6634c041056ec6fc30c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?MACHIZAUD=20Andr=C3=A9a?= Date: Fri, 22 Apr 2022 10:46:34 +0200 Subject: [PATCH 2/3] PoC on `x-add-version` --- include/vcpkg/registries.h | 22 +++++++ include/vcpkg/vcpkgpaths.h | 2 + src/vcpkg/commands.add-version.cpp | 55 ++++++++-------- src/vcpkg/registries.cpp | 100 +++++++++++++++++++++++------ src/vcpkg/vcpkgpaths.cpp | 12 +++- 5 files changed, 143 insertions(+), 48 deletions(-) diff --git a/include/vcpkg/registries.h b/include/vcpkg/registries.h index 351a2a9739..74f5e1254a 100644 --- a/include/vcpkg/registries.h +++ b/include/vcpkg/registries.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -196,4 +197,25 @@ namespace vcpkg private: VersionDbEntryDeserializer underlying; }; + + struct CommandRegistryPaths + { + Path root_path; + Path git_directory_path; + Path ports_directory_path; + Path version_directory_path; + }; + + CommandRegistryPaths resolve_command_registry_paths(const Filesystem& fs, + const VcpkgPaths& paths, + const VcpkgCmdArguments& args, + Build::Editable is_editable); + + ExpectedS>> get_registry_versions( + const VcpkgPaths& paths, const CommandRegistryPaths& registry_paths, StringView port_name); + + ExpectedS>> get_registry_baseline( + const VcpkgPaths& paths, const CommandRegistryPaths& registry_paths); + + GitConfig get_registry_git_config(const VcpkgPaths& paths, const CommandRegistryPaths& registry_paths); } diff --git a/include/vcpkg/vcpkgpaths.h b/include/vcpkg/vcpkgpaths.h index 6a164c1ae0..189ea8457d 100644 --- a/include/vcpkg/vcpkgpaths.h +++ b/include/vcpkg/vcpkgpaths.h @@ -142,6 +142,8 @@ namespace vcpkg const DownloadManager& get_download_manager() const; + ExpectedS>> git_get_registry_port_treeish_map( + const Path& ports_dir) const; ExpectedS>> git_get_local_port_treeish_map() const; // Git manipulation for remote registries diff --git a/src/vcpkg/commands.add-version.cpp b/src/vcpkg/commands.add-version.cpp index b3def68905..927e1d755a 100644 --- a/src/vcpkg/commands.add-version.cpp +++ b/src/vcpkg/commands.add-version.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -274,16 +275,20 @@ namespace return UpdateResult::Updated; } - static UpdateResult update_version_db_file(const VcpkgPaths& paths, + static UpdateResult update_version_db_file(const CommandRegistryPaths& registry_paths, + const VcpkgPaths& paths, const std::string& port_name, const SchemedVersion& port_version, const std::string& git_tree, - const Path& version_db_file_path, bool overwrite_version, bool print_success, bool keep_going, bool skip_version_format_check) { + char prefix[] = {port_name[0], '-', '\0'}; + auto version_db_file_path = + registry_paths.version_directory_path / prefix / Strings::concat(port_name, ".json"); + auto& fs = paths.get_filesystem(); if (!fs.exists(version_db_file_path, IgnoreErrors{})) { @@ -305,7 +310,7 @@ namespace return UpdateResult::Updated; } - auto maybe_versions = get_builtin_versions(paths, port_name); + auto maybe_versions = get_registry_versions(paths, registry_paths, port_name); if (auto versions = maybe_versions.get()) { const auto& versions_end = versions->end(); @@ -431,13 +436,21 @@ namespace vcpkg::Commands::AddVersion const bool verbose = !add_all || Util::Sets::contains(parsed_args.switches, OPTION_VERBOSE); auto& fs = paths.get_filesystem(); - // TODO: Handle consistent port access - auto baseline_path = paths.builtin_registry_versions / "baseline.json"; + CommandRegistryPaths registry_paths = resolve_command_registry_paths(fs, paths, args, Build::Editable::YES); + + auto baseline_path = registry_paths.version_directory_path / "baseline.json"; if (!fs.exists(baseline_path, IgnoreErrors{})) { Checks::msg_exit_with_error(VCPKG_LINE_INFO, msgAddVersionFileNotFound, msg::path = baseline_path); } + auto git_config = get_registry_git_config(paths, registry_paths); + if (!fs.exists(git_config.git_dir, VCPKG_LINE_INFO)) + { + vcpkg::printf(Color::error, "Error: Couldn't find required directory `%s`\n.", git_config.git_dir); + Checks::exit_fail(VCPKG_LINE_INFO); + } + std::vector port_names; if (!args.command_arguments.empty()) { @@ -455,8 +468,8 @@ namespace vcpkg::Commands::AddVersion msg::command_name = "x-add-version", msg::option = OPTION_ALL); - // TODO: Handle consistent port access - for (auto&& port_dir : fs.get_directories_non_recursive(paths.builtin_ports_directory(), VCPKG_LINE_INFO)) + for (auto&& port_dir : + fs.get_directories_non_recursive(registry_paths.ports_directory_path, VCPKG_LINE_INFO)) { port_names.emplace_back(port_dir.stem().to_string()); } @@ -468,18 +481,16 @@ namespace vcpkg::Commands::AddVersion std::map> ret; return ret; } - // TODO: Handle consistent port access - auto maybe_baseline_map = vcpkg::get_builtin_baseline(paths); + auto maybe_baseline_map = get_registry_baseline(paths, registry_paths); return maybe_baseline_map.value_or_exit(VCPKG_LINE_INFO); }(); // Get tree-ish from local repository state. - auto maybe_git_tree_map = paths.git_get_local_port_treeish_map(); + auto maybe_git_tree_map = paths.git_get_registry_port_treeish_map(registry_paths.ports_directory_path); auto git_tree_map = maybe_git_tree_map.value_or_exit(VCPKG_LINE_INFO); // Find ports with uncommited changes std::set changed_ports; - auto git_config = paths.git_builtin_config(); auto maybe_changes = git_ports_with_uncommitted_changes(git_config); if (auto changes = maybe_changes.get()) { @@ -492,8 +503,7 @@ namespace vcpkg::Commands::AddVersion for (auto&& port_name : port_names) { - // TODO: Handle consistent port access - auto port_dir = paths.builtin_ports_directory() / port_name; + auto port_dir = registry_paths.ports_directory_path / port_name; if (!fs.exists(port_dir, IgnoreErrors{})) { @@ -502,8 +512,8 @@ namespace vcpkg::Commands::AddVersion continue; } - // TODO: Handle consistent port access - auto maybe_scf = Paragraphs::try_load_port(fs, paths.builtin_ports_directory() / port_name); + // Get version information of the local port + auto maybe_scf = Paragraphs::try_load_port(fs, registry_paths.ports_directory_path / port_name); if (!maybe_scf.has_value()) { msg::print_error(msgAddVersionLoadPortFailed, msg::package_name = port_name); @@ -517,8 +527,7 @@ namespace vcpkg::Commands::AddVersion if (!skip_formatting_check) { // check if manifest file is property formatted - // TODO: Handle consistent port access - const auto path_to_manifest = paths.builtin_ports_directory() / port_name / "vcpkg.json"; + const auto path_to_manifest = registry_paths.ports_directory_path / port_name / "vcpkg.json"; if (fs.exists(path_to_manifest, IgnoreErrors{})) { const auto current_file_content = fs.read_contents(path_to_manifest, VCPKG_LINE_INFO); @@ -526,7 +535,7 @@ namespace vcpkg::Commands::AddVersion const auto formatted_content = Json::stringify(json, {}); if (current_file_content != formatted_content) { - auto command_line = fmt::format("vcpkg format-manifest ports/{}/vcpkg.json", port_name); + auto command_line = fmt::format("vcpkg format-port {}", port_name); msg::print_error( msg::format(msgAddVersionPortHasImproperFormat, msg::package_name = port_name) .appendnl() @@ -562,21 +571,17 @@ namespace vcpkg::Commands::AddVersion Checks::check_exit(VCPKG_LINE_INFO, !add_all); continue; } - const auto& git_tree = git_tree_it->second; - char prefix[] = {port_name[0], '-', '\0'}; - // TODO: Handle consistent port access - auto port_versions_path = paths.builtin_registry_versions / prefix / Strings::concat(port_name, ".json"); - auto updated_versions_file = update_version_db_file(paths, + const auto& git_tree = git_tree_it->second; + auto updated_versions_file = update_version_db_file(registry_paths, + paths, port_name, schemed_version, git_tree, - port_versions_path, overwrite_version, verbose, add_all, skip_version_format_check); - // TODO: Handle consistent port access auto updated_baseline_file = update_baseline_version( paths, port_name, schemed_version.version, baseline_path, baseline_map, verbose); if (verbose && updated_versions_file == UpdateResult::NotUpdated && diff --git a/src/vcpkg/registries.cpp b/src/vcpkg/registries.cpp index 4b04a6bb30..55a76d8196 100644 --- a/src/vcpkg/registries.cpp +++ b/src/vcpkg/registries.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -1194,30 +1195,12 @@ namespace vcpkg ExpectedS>> get_builtin_versions(const VcpkgPaths& paths, StringView port_name) { - auto maybe_versions = - load_versions_file(paths.get_filesystem(), VersionDbType::Git, paths.builtin_registry_versions, port_name); - if (auto pversions = maybe_versions.get()) - { - return Util::fmap( - *pversions, [](auto&& entry) -> auto { - return std::make_pair(SchemedVersion{entry.scheme, entry.version}, entry.git_tree); - }); - } - - return maybe_versions.error(); + return get_registry_versions(paths, CommandRegistryPaths{{}, {}, paths.builtin_registry_versions}, port_name); } ExpectedS get_builtin_baseline(const VcpkgPaths& paths) { - return load_baseline_versions(paths.get_filesystem(), paths.builtin_registry_versions / "baseline.json") - .then([&](Optional&& b) -> ExpectedS { - if (auto p = b.get()) - { - return std::move(*p); - } - return Strings::concat( - "Error: The baseline file at versions/baseline.json was invalid (no \"default\" field)"); - }); + return get_registry_baseline(paths, CommandRegistryPaths{{}, {}, paths.builtin_registry_versions}); } bool is_git_commit_sha(StringView sv) @@ -1266,4 +1249,81 @@ namespace vcpkg { return std::make_unique(fs, std::move(path), std::move(baseline)); } + + CommandRegistryPaths resolve_command_registry_paths(const Filesystem& fs, + const VcpkgPaths& paths, + const VcpkgCmdArguments& /*args*/, + Build::Editable /*is_editable*/) + { + if (fs.exists(paths.original_cwd / "versions" / "baseline.json", VCPKG_LINE_INFO)) + { + // TODO: Instantiate a registry to know whether it is editable or not + return CommandRegistryPaths{paths.original_cwd, + paths.original_cwd / ".git", + paths.original_cwd / "ports", + paths.original_cwd / "versions"}; + } + else if (fs.exists(paths.original_cwd / "vcpkg.json", VCPKG_LINE_INFO)) + { + vcpkg::printf( + Color::error, "Error: Manifest mode not supported (registry path: %s)\n.", paths.original_cwd); + Checks::exit_fail(VCPKG_LINE_INFO); + } + // NOTE According to registries.cpp:make_builtin_registry this is how we end up with a builtin filesystem + // registry + else if (!paths.use_git_default_registry()) + { + // TODO: Instantiate a registry to know whether it is editable or not + return CommandRegistryPaths{ + paths.builtin_ports_directory().parent_path(), // TODO: Reuse vcpkgpaths.cpp:determine_root + Path(paths.builtin_ports_directory().parent_path()) / + ".git", // TODO: Reuse vcpkgpaths.cpp:determine_root + paths.builtin_ports_directory(), + paths.builtin_registry_versions}; + } + else + { + vcpkg::printf( + Color::error, "Error: Registry paths could not be resolved (registry path: %s)\n.", paths.original_cwd); + Checks::exit_fail(VCPKG_LINE_INFO); + } + } + + ExpectedS get_registry_baseline(const VcpkgPaths& paths, const CommandRegistryPaths& registry_paths) + { + return load_baseline_versions(paths.get_filesystem(), registry_paths.version_directory_path / "baseline.json") + .then([&](Optional&& b) -> ExpectedS { + if (auto p = b.get()) + { + return std::move(*p); + } + return Strings::concat( + "Error: The baseline file at versions/baseline.json was invalid (no \"default\" field)"); + }); + } + + ExpectedS>> get_registry_versions( + const VcpkgPaths& paths, const CommandRegistryPaths& registry_paths, StringView port_name) + { + auto maybe_versions = load_versions_file( + paths.get_filesystem(), VersionDbType::Git, registry_paths.version_directory_path, port_name); + if (auto pversions = maybe_versions.get()) + { + return Util::fmap( + *pversions, [](auto&& entry) -> auto { + return std::make_pair(SchemedVersion{entry.scheme, entry.version}, entry.git_tree); + }); + } + + return maybe_versions.error(); + } + + GitConfig get_registry_git_config(const VcpkgPaths& paths, const CommandRegistryPaths& registry_paths) + { + GitConfig conf; + conf.git_exe = paths.get_tool_exe(Tools::GIT); + conf.git_dir = registry_paths.root_path / ".git"; + conf.git_work_tree = registry_paths.root_path; + return conf; + } } diff --git a/src/vcpkg/vcpkgpaths.cpp b/src/vcpkg/vcpkgpaths.cpp index e8120aa2c6..7dd71e19a1 100644 --- a/src/vcpkg/vcpkgpaths.cpp +++ b/src/vcpkg/vcpkgpaths.cpp @@ -1017,10 +1017,15 @@ namespace vcpkg ExpectedS>> VcpkgPaths::git_get_local_port_treeish_map() const { - const auto local_repo = this->root / ".git"; + return git_get_registry_port_treeish_map(this->builtin_ports_directory()); + } + + ExpectedS>> VcpkgPaths::git_get_registry_port_treeish_map( + const Path& ports_dir) const + { const auto git_cmd = git_cmd_builder({}, {}) .string_arg("-C") - .string_arg(this->builtin_ports_directory()) + .string_arg(ports_dir) .string_arg("ls-tree") .string_arg("-d") .string_arg("HEAD") @@ -1028,7 +1033,8 @@ namespace vcpkg auto output = cmd_execute_and_capture_output(git_cmd); if (output.exit_code != 0) - return Strings::format("Error: Couldn't get local treeish objects for ports.\n%s", output.output); + return Strings::format( + "Error: Couldn't get treeish objects for ports from ports directory %s.\n%s", ports_dir, output.output); std::map> ret; const auto lines = Strings::split(output.output, '\n'); From 69bb1f4c7ae4691b86dcad484e48bf08b5a3bc7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?MACHIZAUD=20Andr=C3=A9a?= Date: Fri, 22 Apr 2022 11:10:39 +0200 Subject: [PATCH 3/3] Add command `format-port` --- include/vcpkg/commands.format-port.h | 14 ++ src/vcpkg/commands.cpp | 3 + src/vcpkg/commands.format-port.cpp | 294 +++++++++++++++++++++++++++ 3 files changed, 311 insertions(+) create mode 100644 include/vcpkg/commands.format-port.h create mode 100644 src/vcpkg/commands.format-port.cpp diff --git a/include/vcpkg/commands.format-port.h b/include/vcpkg/commands.format-port.h new file mode 100644 index 0000000000..1a1e25491a --- /dev/null +++ b/include/vcpkg/commands.format-port.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace vcpkg::Commands::FormatPort +{ + extern const CommandStructure COMMAND_STRUCTURE; + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths); + + struct FormatPortCommand : PathsCommand + { + virtual void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) const override; + }; +} diff --git a/src/vcpkg/commands.cpp b/src/vcpkg/commands.cpp index 56145fbbdc..01cd8beb12 100644 --- a/src/vcpkg/commands.cpp +++ b/src/vcpkg/commands.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -93,6 +94,7 @@ namespace vcpkg::Commands static const Fetch::FetchCommand fetch{}; static const FindCommand find_{}; static const FormatManifest::FormatManifestCommand format_manifest{}; + static const FormatPort::FormatPortCommand format_port{}; static const Help::HelpCommand help{}; static const Info::InfoCommand info{}; static const Integrate::IntegrateCommand integrate{}; @@ -121,6 +123,7 @@ namespace vcpkg::Commands {"fetch", &fetch}, {"find", &find_}, {"format-manifest", &format_manifest}, + {"format-port", &format_port}, {"integrate", &integrate}, {"list", &list}, {"new", &new_}, diff --git a/src/vcpkg/commands.format-port.cpp b/src/vcpkg/commands.format-port.cpp new file mode 100644 index 0000000000..aafa006c17 --- /dev/null +++ b/src/vcpkg/commands.format-port.cpp @@ -0,0 +1,294 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace +{ + using namespace vcpkg; + + struct ToWrite + { + SourceControlFile scf; + Path file_to_write; + Path original_path; + std::string original_source; + }; + + Optional read_manifest(Filesystem& fs, Path&& manifest_path) + { + auto path_string = manifest_path.native(); + Debug::print("Reading ", path_string, "\n"); + auto contents = fs.read_contents(manifest_path, VCPKG_LINE_INFO); + auto parsed_json_opt = Json::parse(contents, manifest_path); + if (!parsed_json_opt.has_value()) + { + vcpkg::printf(Color::error, "Failed to parse %s: %s\n", path_string, parsed_json_opt.error()->format()); + return nullopt; + } + + const auto& parsed_json = parsed_json_opt.value_or_exit(VCPKG_LINE_INFO).first; + if (!parsed_json.is_object()) + { + vcpkg::printf(Color::error, "The file %s is not an object\n", path_string); + return nullopt; + } + + auto parsed_json_obj = parsed_json.object(); + + auto scf = SourceControlFile::parse_manifest_object(manifest_path, parsed_json_obj); + if (!scf.has_value()) + { + vcpkg::printf(Color::error, "Failed to parse manifest file: %s\n", path_string); + print_error_message(scf.error()); + return nullopt; + } + + return ToWrite{ + std::move(*scf.value_or_exit(VCPKG_LINE_INFO)), + manifest_path, + manifest_path, + std::move(contents), + }; + } + + Optional read_control_file(Filesystem& fs, Path&& control_path) + { + std::error_code ec; + Debug::print("Reading ", control_path, "\n"); + + auto manifest_path = Path(control_path.parent_path()) / "vcpkg.json"; + auto contents = fs.read_contents(control_path, VCPKG_LINE_INFO); + auto paragraphs = Paragraphs::parse_paragraphs(contents, control_path); + + if (!paragraphs) + { + vcpkg::printf(Color::error, "Failed to read paragraphs from %s: %s\n", control_path, paragraphs.error()); + return {}; + } + auto scf_res = + SourceControlFile::parse_control_file(control_path, std::move(paragraphs).value_or_exit(VCPKG_LINE_INFO)); + if (!scf_res) + { + vcpkg::printf(Color::error, "Failed to parse control file: %s\n", control_path); + print_error_message(scf_res.error()); + return {}; + } + + return ToWrite{ + std::move(*scf_res.value_or_exit(VCPKG_LINE_INFO)), + manifest_path, + control_path, + std::move(contents), + }; + } + + void open_for_write(Filesystem& fs, const ToWrite& data) + { + const auto& original_path_string = data.original_path.native(); + const auto& file_to_write_string = data.file_to_write.native(); + if (data.file_to_write == data.original_path) + { + Debug::print("Formatting ", file_to_write_string, "\n"); + } + else + { + Debug::print("Converting ", file_to_write_string, " -> ", original_path_string, "\n"); + } + auto res = serialize_manifest(data.scf); + + auto check = SourceControlFile::parse_manifest_object(StringView{}, res); + if (!check) + { + vcpkg::printf(Color::error, + R"([correctness check] Failed to parse serialized manifest file of %s +Please open an issue at https://github.com/microsoft/vcpkg, with the following output: +Error:)", + data.scf.core_paragraph->name); + print_error_message(check.error()); + Checks::exit_maybe_upgrade(VCPKG_LINE_INFO, + R"( +=== Serialized manifest file === +%s +)", + Json::stringify(res, {})); + } + + auto check_scf = std::move(check).value_or_exit(VCPKG_LINE_INFO); + if (*check_scf != data.scf) + { + Checks::exit_maybe_upgrade( + VCPKG_LINE_INFO, + R"([correctness check] The serialized manifest SCF was different from the original SCF. +Please open an issue at https://github.com/microsoft/vcpkg, with the following output: + +=== Original File === +%s + +=== Serialized File === +%s + +=== Original SCF === +%s + +=== Serialized SCF === +%s +)", + data.original_source, + Json::stringify(res, {}), + Json::stringify(serialize_debug_manifest(data.scf), {}), + Json::stringify(serialize_debug_manifest(*check_scf), {})); + } + + // the manifest scf is correct + std::error_code ec; + fs.write_contents(data.file_to_write, Json::stringify(res, {}), ec); + if (ec) + { + Checks::exit_with_message( + VCPKG_LINE_INFO, "Failed to write manifest file %s: %s\n", file_to_write_string, ec.message()); + } + if (data.original_path != data.file_to_write) + { + fs.remove(data.original_path, ec); + if (ec) + { + Checks::exit_with_message( + VCPKG_LINE_INFO, "Failed to remove control file %s: %s\n", original_path_string, ec.message()); + } + } + } +} + +namespace vcpkg::Commands::FormatPort +{ + static constexpr StringLiteral OPTION_ALL = "all"; + static constexpr StringLiteral OPTION_CONVERT_CONTROL = "convert-control"; + + const CommandSwitch FORMAT_SWITCHES[] = { + {OPTION_ALL, "Format all ports' manifest files."}, + {OPTION_CONVERT_CONTROL, "Convert CONTROL files to manifest files."}, + }; + + const CommandStructure COMMAND_STRUCTURE = { + create_example_string(R"###(format-port --all)###"), + 0, + SIZE_MAX, + {FORMAT_SWITCHES, {}, {}}, + nullptr, + }; + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + { + auto parsed_args = args.parse_arguments(COMMAND_STRUCTURE); + + auto& fs = paths.get_filesystem(); + CommandRegistryPaths registry_paths = resolve_command_registry_paths(fs, paths, args, Build::Editable::YES); + + bool has_error = false; + + const bool format_all = Util::Sets::contains(parsed_args.switches, OPTION_ALL); + const bool convert_control = Util::Sets::contains(parsed_args.switches, OPTION_CONVERT_CONTROL); + + if (!format_all && convert_control) + { + print2(Color::warning, R"(format-port was passed '--convert-control' without '--all'. + This doesn't do anything: + we will automatically convert all control files passed explicitly.)"); + } + + if (!format_all && args.command_arguments.empty()) + { + Checks::exit_with_message( + VCPKG_LINE_INFO, + "No files to format; please pass either --all, or the explicit files to format or convert."); + } + + std::vector to_write; + + const auto add_file = [&to_write, &has_error](Optional&& opt) { + if (auto t = opt.get()) + to_write.push_back(std::move(*t)); + else + has_error = true; + }; + + for (auto&& port_name : args.command_arguments) + { + auto port_path = registry_paths.ports_directory_path / port_name; + + if (!fs.exists(port_path, VCPKG_LINE_INFO)) + { + vcpkg::printf(Color::error, "Error: Couldn't find required port `%s`\n.", port_name); + has_error = true; + continue; + } + + auto port_control_path = port_path / "CONTROL"; + auto port_manifest_path = port_path / "vcpkg.json"; + + if (fs.exists(port_control_path, VCPKG_LINE_INFO)) + { + add_file(read_control_file(fs, std::move(port_control_path))); + } + else + { + add_file(read_manifest(fs, std::move(port_manifest_path))); + } + } + + if (format_all) + { + for (const auto& dir : + fs.get_directories_non_recursive(registry_paths.ports_directory_path, VCPKG_LINE_INFO)) + { + auto control_path = dir / "CONTROL"; + auto manifest_path = dir / "vcpkg.json"; + auto manifest_exists = fs.exists(manifest_path, IgnoreErrors{}); + auto control_exists = fs.exists(control_path, IgnoreErrors{}); + + Checks::check_exit(VCPKG_LINE_INFO, + !manifest_exists || !control_exists, + "Both a manifest file and a CONTROL file exist in port directory: %s", + dir); + + if (manifest_exists) + { + add_file(read_manifest(fs, std::move(manifest_path))); + } + if (convert_control && control_exists) + { + add_file(read_control_file(fs, std::move(control_path))); + } + } + } + + for (auto const& el : to_write) + { + open_for_write(fs, el); + } + + if (has_error) + { + Checks::exit_fail(VCPKG_LINE_INFO); + } + else + { + print2("Succeeded in formatting the manifest files.\n"); + Checks::exit_success(VCPKG_LINE_INFO); + } + } + + void FormatPortCommand::perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) const + { + FormatPort::perform_and_exit(args, paths); + } +}