Skip to content

Commit

Permalink
Add command format-port
Browse files Browse the repository at this point in the history
  • Loading branch information
theblackunknown committed May 9, 2022
1 parent a6d4514 commit 5ffa68c
Show file tree
Hide file tree
Showing 3 changed files with 311 additions and 0 deletions.
14 changes: 14 additions & 0 deletions include/vcpkg/commands.format-port.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <vcpkg/commands.interface.h>

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;
};
}
3 changes: 3 additions & 0 deletions src/vcpkg/commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <vcpkg/commands.fetch.h>
#include <vcpkg/commands.find.h>
#include <vcpkg/commands.format-manifest.h>
#include <vcpkg/commands.format-port.h>
#include <vcpkg/commands.generate-message-map.h>
#include <vcpkg/commands.h>
#include <vcpkg/commands.hash.h>
Expand Down Expand Up @@ -92,6 +93,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{};
Expand Down Expand Up @@ -119,6 +121,7 @@ namespace vcpkg::Commands
{"fetch", &fetch},
{"find", &find_},
{"format-manifest", &format_manifest},
{"format-port", &format_port},
{"integrate", &integrate},
{"list", &list},
{"new", &new_},
Expand Down
294 changes: 294 additions & 0 deletions src/vcpkg/commands.format-port.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
#include <vcpkg/base/checks.h>
#include <vcpkg/base/files.h>
#include <vcpkg/base/json.h>
#include <vcpkg/base/system.debug.h>
#include <vcpkg/base/system.print.h>

#include <vcpkg/commands.format-port.h>
#include <vcpkg/paragraphs.h>
#include <vcpkg/portfileprovider.h>
#include <vcpkg/sourceparagraph.h>
#include <vcpkg/vcpkgcmdarguments.h>
#include <vcpkg/vcpkgpaths.h>

namespace
{
using namespace vcpkg;

struct ToWrite
{
SourceControlFile scf;
Path file_to_write;
Path original_path;
std::string original_source;
};

Optional<ToWrite> 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<ToWrite> 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<ToWrite> to_write;

const auto add_file = [&to_write, &has_error](Optional<ToWrite>&& 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);
}
}

0 comments on commit 5ffa68c

Please sign in to comment.