Make status line parsing testable. (#1528)

Removes Want::UNKNOWN as this was only ever referenced in to_string, and never otherwise used in the product.
This commit is contained in:
Billy O'Neal 2024-11-04 14:34:17 -08:00 коммит произвёл GitHub
Родитель 360e5c64da
Коммит 84e2dcfb2e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
16 изменённых файлов: 226 добавлений и 103 удалений

Просмотреть файл

@ -581,5 +581,4 @@ namespace vcpkg
inline constexpr StringLiteral StatusInstalled = "installed"; inline constexpr StringLiteral StatusInstalled = "installed";
inline constexpr StringLiteral StatusNotInstalled = "not-installed"; inline constexpr StringLiteral StatusNotInstalled = "not-installed";
inline constexpr StringLiteral StatusPurge = "purge"; inline constexpr StringLiteral StatusPurge = "purge";
inline constexpr StringLiteral StatusUnknown = "unknown";
} }

Просмотреть файл

@ -1172,6 +1172,10 @@ DECLARE_MESSAGE(ExpectedFailOrSkip, (), "", "expected 'fail', 'skip', or 'pass'
DECLARE_MESSAGE(ExpectedFeatureListTerminal, (), "", "expected ',' or ']' in feature list") DECLARE_MESSAGE(ExpectedFeatureListTerminal, (), "", "expected ',' or ']' in feature list")
DECLARE_MESSAGE(ExpectedFeatureName, (), "", "expected feature name (must be lowercase, digits, '-')") DECLARE_MESSAGE(ExpectedFeatureName, (), "", "expected feature name (must be lowercase, digits, '-')")
DECLARE_MESSAGE(ExpectedExplicitTriplet, (), "", "expected an explicit triplet") DECLARE_MESSAGE(ExpectedExplicitTriplet, (), "", "expected an explicit triplet")
DECLARE_MESSAGE(ExpectedInstallStateField,
(),
"The values in ''s are locale-invariant",
"expected one of 'not-installed', 'half-installed', or 'installed'")
DECLARE_MESSAGE(ExpectedOneSetOfTags, DECLARE_MESSAGE(ExpectedOneSetOfTags,
(msg::count, msg::old_value, msg::new_value, msg::value), (msg::count, msg::old_value, msg::new_value, msg::value),
"{old_value} is a left tag and {new_value} is the right tag. {value} is the input.", "{old_value} is a left tag and {new_value} is the right tag. {value} is the input.",
@ -1181,7 +1185,15 @@ DECLARE_MESSAGE(ExpectedPathToExist, (msg::path), "", "Expected {path} to exist
DECLARE_MESSAGE(ExpectedPortName, (), "", "expected a port name here (must be lowercase, digits, '-')") DECLARE_MESSAGE(ExpectedPortName, (), "", "expected a port name here (must be lowercase, digits, '-')")
DECLARE_MESSAGE(ExpectedReadWriteReadWrite, (), "", "unexpected argument: expected 'read', readwrite', or 'write'") DECLARE_MESSAGE(ExpectedReadWriteReadWrite, (), "", "unexpected argument: expected 'read', readwrite', or 'write'")
DECLARE_MESSAGE(ExpectedStatusField, (), "", "Expected 'status' field in status paragraph") DECLARE_MESSAGE(ExpectedStatusField, (), "", "Expected 'status' field in status paragraph")
DECLARE_MESSAGE(ExpectedTextHere,
(msg::expected),
"{expected} is a locale-invariant string a parser was searching for",
"expected '{expected}' here")
DECLARE_MESSAGE(ExpectedTripletName, (), "", "expected a triplet name here (must be lowercase, digits, '-')") DECLARE_MESSAGE(ExpectedTripletName, (), "", "expected a triplet name here (must be lowercase, digits, '-')")
DECLARE_MESSAGE(ExpectedWantField,
(),
"The values in ''s are locale-invariant",
"expected one of 'install', 'hold', 'deinstall', or 'purge' here")
DECLARE_MESSAGE(ExportArchitectureReq, DECLARE_MESSAGE(ExportArchitectureReq,
(), (),
"", "",

Просмотреть файл

@ -90,6 +90,7 @@ namespace vcpkg
} }
bool require_character(char ch); bool require_character(char ch);
bool require_text(StringLiteral keyword);
bool try_match_keyword(StringView keyword_content); bool try_match_keyword(StringView keyword_content);

Просмотреть файл

@ -13,13 +13,13 @@ namespace vcpkg
enum class Want enum class Want
{ {
ERROR_STATE, ERROR_STATE,
UNKNOWN,
INSTALL, INSTALL,
HOLD, HOLD,
DEINSTALL, DEINSTALL,
PURGE PURGE
}; };
struct StatusLine;
struct StatusParagraph; struct StatusParagraph;
struct InstalledPackageView; struct InstalledPackageView;
} }

Просмотреть файл

@ -9,6 +9,7 @@
#include <vcpkg/sourceparagraph.h> #include <vcpkg/sourceparagraph.h>
#include <string>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -24,6 +25,8 @@ namespace vcpkg::Paragraphs
ExpectedL<std::vector<Paragraph>> parse_paragraphs(StringView str, StringView origin); ExpectedL<std::vector<Paragraph>> parse_paragraphs(StringView str, StringView origin);
void append_paragraph_field(StringView name, StringView field, std::string& out_str);
bool is_port_directory(const ReadOnlyFilesystem& fs, const Path& maybe_directory); bool is_port_directory(const ReadOnlyFilesystem& fs, const Path& maybe_directory);
struct PortLoadResult struct PortLoadResult

Просмотреть файл

@ -5,6 +5,8 @@
#include <vcpkg/fwd/installedpaths.h> #include <vcpkg/fwd/installedpaths.h>
#include <vcpkg/fwd/statusparagraph.h> #include <vcpkg/fwd/statusparagraph.h>
#include <vcpkg/base/fmt.h>
#include <vcpkg/binaryparagraph.h> #include <vcpkg/binaryparagraph.h>
#include <map> #include <map>
@ -13,18 +15,35 @@
namespace vcpkg namespace vcpkg
{ {
struct StatusLine
{
Want want = Want::ERROR_STATE;
InstallState state = InstallState::ERROR_STATE;
bool is_installed() const noexcept { return want == Want::INSTALL && state == InstallState::INSTALLED; }
void to_string(std::string& out) const;
std::string to_string() const;
friend bool operator==(const StatusLine& lhs, const StatusLine& rhs)
{
return lhs.want == rhs.want && lhs.state == rhs.state;
}
friend bool operator!=(const StatusLine& lhs, const StatusLine& rhs) { return !(lhs == rhs); }
};
ExpectedL<StatusLine> parse_status_line(StringView text, Optional<StringView> origin, TextRowCol init_rowcol);
// metadata for a package's representation in the 'installed' tree // metadata for a package's representation in the 'installed' tree
struct StatusParagraph struct StatusParagraph
{ {
StatusParagraph() noexcept; StatusParagraph() = default;
StatusParagraph(StringView origin, Paragraph&& fields); StatusParagraph(StringView origin, Paragraph&& fields);
bool is_installed() const { return want == Want::INSTALL && state == InstallState::INSTALLED; } bool is_installed() const noexcept { return status.is_installed(); }
BinaryParagraph package; BinaryParagraph package;
Want want; StatusLine status;
InstallState state;
}; };
void serialize(const StatusParagraph& pgh, std::string& out_str); void serialize(const StatusParagraph& pgh, std::string& out_str);
@ -59,3 +78,4 @@ namespace vcpkg
VCPKG_FORMAT_WITH_TO_STRING_LITERAL_NONMEMBER(vcpkg::InstallState); VCPKG_FORMAT_WITH_TO_STRING_LITERAL_NONMEMBER(vcpkg::InstallState);
VCPKG_FORMAT_WITH_TO_STRING_LITERAL_NONMEMBER(vcpkg::Want); VCPKG_FORMAT_WITH_TO_STRING_LITERAL_NONMEMBER(vcpkg::Want);
VCPKG_FORMAT_WITH_TO_STRING(vcpkg::StatusLine);

Просмотреть файл

@ -691,6 +691,8 @@
"ExpectedFailOrSkip": "expected 'fail', 'skip', or 'pass' here", "ExpectedFailOrSkip": "expected 'fail', 'skip', or 'pass' here",
"ExpectedFeatureListTerminal": "expected ',' or ']' in feature list", "ExpectedFeatureListTerminal": "expected ',' or ']' in feature list",
"ExpectedFeatureName": "expected feature name (must be lowercase, digits, '-')", "ExpectedFeatureName": "expected feature name (must be lowercase, digits, '-')",
"ExpectedInstallStateField": "expected one of 'not-installed', 'half-installed', or 'installed'",
"_ExpectedInstallStateField.comment": "The values in ''s are locale-invariant",
"ExpectedOneSetOfTags": "Found {count} sets of {old_value}.*{new_value} but expected exactly 1, in block:\n{value}", "ExpectedOneSetOfTags": "Found {count} sets of {old_value}.*{new_value} but expected exactly 1, in block:\n{value}",
"_ExpectedOneSetOfTags.comment": "{old_value} is a left tag and {new_value} is the right tag. {value} is the input. An example of {count} is 42.", "_ExpectedOneSetOfTags.comment": "{old_value} is a left tag and {new_value} is the right tag. {value} is the input. An example of {count} is 42.",
"ExpectedOneVersioningField": "expected only one versioning field", "ExpectedOneVersioningField": "expected only one versioning field",
@ -699,7 +701,11 @@
"ExpectedPortName": "expected a port name here (must be lowercase, digits, '-')", "ExpectedPortName": "expected a port name here (must be lowercase, digits, '-')",
"ExpectedReadWriteReadWrite": "unexpected argument: expected 'read', readwrite', or 'write'", "ExpectedReadWriteReadWrite": "unexpected argument: expected 'read', readwrite', or 'write'",
"ExpectedStatusField": "Expected 'status' field in status paragraph", "ExpectedStatusField": "Expected 'status' field in status paragraph",
"ExpectedTextHere": "expected '{expected}' here",
"_ExpectedTextHere.comment": "{expected} is a locale-invariant string a parser was searching for",
"ExpectedTripletName": "expected a triplet name here (must be lowercase, digits, '-')", "ExpectedTripletName": "expected a triplet name here (must be lowercase, digits, '-')",
"ExpectedWantField": "expected one of 'install', 'hold', 'deinstall', or 'purge' here",
"_ExpectedWantField.comment": "The values in ''s are locale-invariant",
"ExportArchitectureReq": "Export prefab requires targeting at least one of the following architectures arm64-v8a, armeabi-v7a, x86_64, x86 to be present.", "ExportArchitectureReq": "Export prefab requires targeting at least one of the following architectures arm64-v8a, armeabi-v7a, x86_64, x86 to be present.",
"ExportPrefabRequiresAndroidTriplet": "export prefab requires an Android triplet.", "ExportPrefabRequiresAndroidTriplet": "export prefab requires an Android triplet.",
"Exported7zipArchive": "7zip archive exported at: {path}", "Exported7zipArchive": "7zip archive exported at: {path}",

Просмотреть файл

@ -9,6 +9,38 @@ using namespace vcpkg;
using namespace vcpkg::Paragraphs; using namespace vcpkg::Paragraphs;
using namespace vcpkg::Test; using namespace vcpkg::Test;
static constexpr StringLiteral test_origin = "test";
static constexpr TextRowCol test_textrowcol = {42, 34};
TEST_CASE ("parse status lines", "[statusparagraphs]")
{
REQUIRE(parse_status_line("install ok installed", test_origin, test_textrowcol).value_or_exit(VCPKG_LINE_INFO) ==
StatusLine{Want::INSTALL, InstallState::INSTALLED});
REQUIRE(parse_status_line("hold ok installed", test_origin, test_textrowcol).value_or_exit(VCPKG_LINE_INFO) ==
StatusLine{Want::HOLD, InstallState::INSTALLED});
REQUIRE(parse_status_line("deinstall ok installed", test_origin, test_textrowcol).value_or_exit(VCPKG_LINE_INFO) ==
StatusLine{Want::DEINSTALL, InstallState::INSTALLED});
REQUIRE(parse_status_line("purge ok installed", test_origin, test_textrowcol).value_or_exit(VCPKG_LINE_INFO) ==
StatusLine{Want::PURGE, InstallState::INSTALLED});
REQUIRE(
parse_status_line("install ok not-installed", test_origin, test_textrowcol).value_or_exit(VCPKG_LINE_INFO) ==
StatusLine{Want::INSTALL, InstallState::NOT_INSTALLED});
REQUIRE(
parse_status_line("install ok half-installed", test_origin, test_textrowcol).value_or_exit(VCPKG_LINE_INFO) ==
StatusLine{Want::INSTALL, InstallState::HALF_INSTALLED});
REQUIRE(parse_status_line("meow ok installed", test_origin, test_textrowcol).error() ==
LocalizedString::from_raw("test:42:34: error: expected one of 'install', 'hold', 'deinstall', or 'purge' "
"here\n on expression: meow ok installed\n ^"));
REQUIRE(parse_status_line("install ko half-installed", test_origin, test_textrowcol).error() ==
LocalizedString::from_raw("test:42:41: error: expected ' ok ' here\n on expression: install ko "
"half-installed\n ^"));
REQUIRE(parse_status_line("install ok meow", test_origin, test_textrowcol).error() ==
LocalizedString::from_raw("test:42:45: error: expected one of 'not-installed', 'half-installed', or "
"'installed'\n on expression: install ok meow\n ^"));
}
TEST_CASE ("find installed", "[statusparagraphs]") TEST_CASE ("find installed", "[statusparagraphs]")
{ {
auto pghs = parse_paragraphs(R"( auto pghs = parse_paragraphs(R"(
@ -23,9 +55,8 @@ Status: install ok installed
REQUIRE(pghs); REQUIRE(pghs);
StatusParagraphs status_db(Util::fmap(*pghs.get(), [](Paragraph& rpgh) { StatusParagraphs status_db(Util::fmap(
return std::make_unique<StatusParagraph>(StringLiteral{"test"}, std::move(rpgh)); *pghs.get(), [](Paragraph& rpgh) { return std::make_unique<StatusParagraph>(test_origin, std::move(rpgh)); }));
}));
auto it = status_db.find_installed({"ffmpeg", Test::X64_WINDOWS}); auto it = status_db.find_installed({"ffmpeg", Test::X64_WINDOWS});
REQUIRE(it != status_db.end()); REQUIRE(it != status_db.end());
@ -45,9 +76,8 @@ Status: purge ok not-installed
REQUIRE(pghs); REQUIRE(pghs);
StatusParagraphs status_db(Util::fmap(*pghs.get(), [](Paragraph& rpgh) { StatusParagraphs status_db(Util::fmap(
return std::make_unique<StatusParagraph>(StringLiteral{"test"}, std::move(rpgh)); *pghs.get(), [](Paragraph& rpgh) { return std::make_unique<StatusParagraph>(test_origin, std::move(rpgh)); }));
}));
auto it = status_db.find_installed({"ffmpeg", Test::X64_WINDOWS}); auto it = status_db.find_installed({"ffmpeg", Test::X64_WINDOWS});
REQUIRE(it == status_db.end()); REQUIRE(it == status_db.end());
@ -75,9 +105,8 @@ Status: purge ok not-installed
REQUIRE(pghs); REQUIRE(pghs);
StatusParagraphs status_db(Util::fmap(*pghs.get(), [](Paragraph& rpgh) { StatusParagraphs status_db(Util::fmap(
return std::make_unique<StatusParagraph>(StringLiteral{"test"}, std::move(rpgh)); *pghs.get(), [](Paragraph& rpgh) { return std::make_unique<StatusParagraph>(test_origin, std::move(rpgh)); }));
}));
auto it = status_db.find_installed({"ffmpeg", Test::X64_WINDOWS}); auto it = status_db.find_installed({"ffmpeg", Test::X64_WINDOWS});
REQUIRE(it != status_db.end()); REQUIRE(it != status_db.end());
@ -108,9 +137,8 @@ Status: install ok installed
"test-origin"); "test-origin");
REQUIRE(pghs); REQUIRE(pghs);
StatusParagraphs status_db(Util::fmap(*pghs.get(), [](Paragraph& rpgh) { StatusParagraphs status_db(Util::fmap(
return std::make_unique<StatusParagraph>(StringLiteral{"test"}, std::move(rpgh)); *pghs.get(), [](Paragraph& rpgh) { return std::make_unique<StatusParagraph>(test_origin, std::move(rpgh)); }));
}));
// Feature "openssl" is installed and should therefore be found // Feature "openssl" is installed and should therefore be found
auto it = status_db.find_installed({{"ffmpeg", Test::X64_WINDOWS}, "openssl"}); auto it = status_db.find_installed({{"ffmpeg", Test::X64_WINDOWS}, "openssl"});

Просмотреть файл

@ -64,8 +64,7 @@ TEST_CASE ("find outdated packages features 2", "[update]")
status_paragraphs.push_back(make_status_feature_pgh("a", "b")); status_paragraphs.push_back(make_status_feature_pgh("a", "b"));
status_paragraphs.back()->package.version = Version{"0", 0}; status_paragraphs.back()->package.version = Version{"0", 0};
status_paragraphs.back()->state = InstallState::NOT_INSTALLED; status_paragraphs.back()->status = {Want::PURGE, InstallState::NOT_INSTALLED};
status_paragraphs.back()->want = Want::PURGE;
StatusParagraphs status_db(std::move(status_paragraphs)); StatusParagraphs status_db(std::move(status_paragraphs));

Просмотреть файл

@ -146,6 +146,27 @@ namespace vcpkg
return true; return true;
} }
bool ParserBase::require_text(StringLiteral text)
{
auto encoded = m_it;
// check that the encoded stream matches the keyword:
for (const char ch : text)
{
if (encoded.is_eof() || *encoded != static_cast<char32_t>(ch))
{
add_error(msg::format(msgExpectedTextHere, msg::expected = text));
return false;
}
++encoded;
}
// success
m_it = encoded;
m_column += static_cast<int>(text.size());
return true;
}
bool ParserBase::try_match_keyword(StringView keyword_content) bool ParserBase::try_match_keyword(StringView keyword_content)
{ {
auto encoded = m_it; auto encoded = m_it;

Просмотреть файл

@ -7,9 +7,12 @@
#include <vcpkg/paragraphparser.h> #include <vcpkg/paragraphparser.h>
#include <vcpkg/paragraphs.h> #include <vcpkg/paragraphs.h>
using namespace vcpkg::Paragraphs;
namespace vcpkg namespace vcpkg
{ {
BinaryParagraph::BinaryParagraph(StringView origin, Paragraph&& fields) BinaryParagraph::BinaryParagraph(StringView origin, Paragraph&& fields)
: spec(), version(), description(), maintainers(), feature(), default_features(), dependencies(), abi()
{ {
ParagraphParser parser(origin, std::move(fields)); ParagraphParser parser(origin, std::move(fields));
this->spec = PackageSpec(parser.required_field(ParagraphIdPackage), this->spec = PackageSpec(parser.required_field(ParagraphIdPackage),
@ -177,15 +180,6 @@ namespace vcpkg
bool operator!=(const BinaryParagraph& lhs, const BinaryParagraph& rhs) { return !(lhs == rhs); } bool operator!=(const BinaryParagraph& lhs, const BinaryParagraph& rhs) { return !(lhs == rhs); }
static void serialize_string(StringView name, const std::string& field, std::string& out_str)
{
if (field.empty())
{
return;
}
out_str.append(name.data(), name.size()).append(": ").append(field).push_back('\n');
}
static void serialize_array(StringView name, static void serialize_array(StringView name,
const std::vector<std::string>& array, const std::vector<std::string>& array,
std::string& out_str, std::string& out_str,
@ -223,9 +217,8 @@ namespace vcpkg
{ {
const size_t initial_end = out_str.size(); const size_t initial_end = out_str.size();
serialize_string(ParagraphIdPackage, pgh.spec.name(), out_str); append_paragraph_field(ParagraphIdPackage, pgh.spec.name(), out_str);
append_paragraph_field(ParagraphIdVersion, pgh.version.text, out_str);
serialize_string(ParagraphIdVersion, pgh.version.text, out_str);
if (pgh.version.port_version != 0) if (pgh.version.port_version != 0)
{ {
fmt::format_to(std::back_inserter(out_str), "{}: {}\n", ParagraphIdPortVersion, pgh.version.port_version); fmt::format_to(std::back_inserter(out_str), "{}: {}\n", ParagraphIdPortVersion, pgh.version.port_version);
@ -233,23 +226,20 @@ namespace vcpkg
if (pgh.is_feature()) if (pgh.is_feature())
{ {
serialize_string(ParagraphIdFeature, pgh.feature, out_str); append_paragraph_field(ParagraphIdFeature, pgh.feature, out_str);
} }
if (!pgh.dependencies.empty()) if (!pgh.dependencies.empty())
{ {
serialize_string(ParagraphIdDepends, serialize_deps_list(pgh.dependencies, pgh.spec.triplet()), out_str); append_paragraph_field(
ParagraphIdDepends, serialize_deps_list(pgh.dependencies, pgh.spec.triplet()), out_str);
} }
serialize_string(ParagraphIdArchitecture, pgh.spec.triplet().to_string(), out_str); append_paragraph_field(ParagraphIdArchitecture, pgh.spec.triplet().to_string(), out_str);
serialize_string(ParagraphIdMultiArch, "same", out_str); append_paragraph_field(ParagraphIdMultiArch, "same", out_str);
serialize_paragraph(ParagraphIdMaintainer, pgh.maintainers, out_str); serialize_paragraph(ParagraphIdMaintainer, pgh.maintainers, out_str);
append_paragraph_field(ParagraphIdAbi, pgh.abi, out_str);
serialize_string(ParagraphIdAbi, pgh.abi, out_str);
serialize_paragraph(ParagraphIdDescription, pgh.description, out_str); serialize_paragraph(ParagraphIdDescription, pgh.description, out_str);
serialize_array(ParagraphIdDefaultFeatures, pgh.default_features, out_str); serialize_array(ParagraphIdDefaultFeatures, pgh.default_features, out_str);
// sanity check the serialized data // sanity check the serialized data

Просмотреть файл

@ -280,8 +280,7 @@ namespace vcpkg
StatusParagraph source_paragraph; StatusParagraph source_paragraph;
source_paragraph.package = bcf.core_paragraph; source_paragraph.package = bcf.core_paragraph;
source_paragraph.want = Want::INSTALL; source_paragraph.status = StatusLine{Want::INSTALL, InstallState::HALF_INSTALLED};
source_paragraph.state = InstallState::HALF_INSTALLED;
write_update(fs, installed, source_paragraph); write_update(fs, installed, source_paragraph);
status_db->insert(std::make_unique<StatusParagraph>(source_paragraph)); status_db->insert(std::make_unique<StatusParagraph>(source_paragraph));
@ -291,8 +290,7 @@ namespace vcpkg
{ {
StatusParagraph& feature_paragraph = features_spghs.emplace_back(); StatusParagraph& feature_paragraph = features_spghs.emplace_back();
feature_paragraph.package = feature; feature_paragraph.package = feature;
feature_paragraph.want = Want::INSTALL; feature_paragraph.status = StatusLine{Want::INSTALL, InstallState::HALF_INSTALLED};
feature_paragraph.state = InstallState::HALF_INSTALLED;
write_update(fs, installed, feature_paragraph); write_update(fs, installed, feature_paragraph);
status_db->insert(std::make_unique<StatusParagraph>(feature_paragraph)); status_db->insert(std::make_unique<StatusParagraph>(feature_paragraph));
@ -303,13 +301,13 @@ namespace vcpkg
install_package_and_write_listfile(fs, package_dir, install_dir); install_package_and_write_listfile(fs, package_dir, install_dir);
source_paragraph.state = InstallState::INSTALLED; source_paragraph.status.state = InstallState::INSTALLED;
write_update(fs, installed, source_paragraph); write_update(fs, installed, source_paragraph);
status_db->insert(std::make_unique<StatusParagraph>(source_paragraph)); status_db->insert(std::make_unique<StatusParagraph>(source_paragraph));
for (auto&& feature_paragraph : features_spghs) for (auto&& feature_paragraph : features_spghs)
{ {
feature_paragraph.state = InstallState::INSTALLED; feature_paragraph.status.state = InstallState::INSTALLED;
write_update(fs, installed, feature_paragraph); write_update(fs, installed, feature_paragraph);
status_db->insert(std::make_unique<StatusParagraph>(feature_paragraph)); status_db->insert(std::make_unique<StatusParagraph>(feature_paragraph));
} }

Просмотреть файл

@ -30,8 +30,7 @@ namespace vcpkg
for (auto&& spgh : spghs) for (auto&& spgh : spghs)
{ {
spgh.want = Want::PURGE; spgh.status = {Want::PURGE, InstallState::HALF_INSTALLED};
spgh.state = InstallState::HALF_INSTALLED;
write_update(fs, installed, spgh); write_update(fs, installed, spgh);
} }
@ -93,9 +92,8 @@ namespace vcpkg
for (auto&& spgh : spghs) for (auto&& spgh : spghs)
{ {
spgh.state = InstallState::NOT_INSTALLED; spgh.status.state = InstallState::NOT_INSTALLED;
write_update(fs, installed, spgh); write_update(fs, installed, spgh);
status_db.insert(std::make_unique<StatusParagraph>(std::move(spgh))); status_db.insert(std::make_unique<StatusParagraph>(std::move(spgh)));
} }
} }

Просмотреть файл

@ -352,6 +352,16 @@ namespace vcpkg::Paragraphs
return PghParser(str, origin).get_paragraphs(); return PghParser(str, origin).get_paragraphs();
} }
void append_paragraph_field(StringView name, StringView field, std::string& out_str)
{
if (field.empty())
{
return;
}
out_str.append(name.data(), name.size()).append(": ").append(field.data(), field.size()).push_back('\n');
}
bool is_port_directory(const ReadOnlyFilesystem& fs, const Path& maybe_directory) bool is_port_directory(const ReadOnlyFilesystem& fs, const Path& maybe_directory)
{ {
return fs.exists(maybe_directory / "CONTROL", IgnoreErrors{}) || return fs.exists(maybe_directory / "CONTROL", IgnoreErrors{}) ||

Просмотреть файл

@ -1,60 +1,94 @@
#include <vcpkg/base/contractual-constants.h> #include <vcpkg/base/contractual-constants.h>
#include <vcpkg/base/util.h> #include <vcpkg/base/util.h>
#include <vcpkg/paragraphs.h>
#include <vcpkg/statusparagraph.h> #include <vcpkg/statusparagraph.h>
using namespace vcpkg::Paragraphs;
namespace vcpkg namespace vcpkg
{ {
StatusParagraph::StatusParagraph() noexcept : want(Want::ERROR_STATE), state(InstallState::ERROR_STATE) { } void StatusLine::to_string(std::string& out) const
void serialize(const StatusParagraph& pgh, std::string& out_str)
{ {
auto want_literal = to_string_literal(pgh.want); fmt::format_to(std::back_inserter(out), "{} ok {}", want, state);
auto state_literal = to_string_literal(pgh.state); }
serialize(pgh.package, out_str);
out_str.append("Status: ") std::string StatusLine::to_string() const { return adapt_to_string(*this); }
.append(want_literal.data(), want_literal.size())
.append(" ok ") ExpectedL<StatusLine> parse_status_line(StringView text, Optional<StringView> origin, TextRowCol init_rowcol)
.append(state_literal.data(), state_literal.size()) {
.push_back('\n'); ParserBase parser{text, origin, init_rowcol};
StatusLine result;
const auto want_start = parser.cur_loc();
auto want_text = parser.match_until(ParserBase::is_whitespace);
if (want_text == StatusInstall)
{
result.want = Want::INSTALL;
}
else if (want_text == StatusHold)
{
result.want = Want::HOLD;
}
else if (want_text == StatusDeinstall)
{
result.want = Want::DEINSTALL;
}
else if (want_text == StatusPurge)
{
result.want = Want::PURGE;
}
else
{
parser.add_error(msg::format(msgExpectedWantField), want_start);
return parser.extract_messages().error.value_or_exit(VCPKG_LINE_INFO);
}
if (parser.require_text(" ok "))
{
auto state_start = parser.cur_loc();
auto state_text = parser.match_until(ParserBase::is_whitespace);
if (state_text == StatusNotInstalled)
{
result.state = InstallState::NOT_INSTALLED;
}
else if (state_text == StatusInstalled)
{
result.state = InstallState::INSTALLED;
}
else if (state_text == StatusHalfInstalled)
{
result.state = InstallState::HALF_INSTALLED;
}
else
{
parser.add_error(msg::format(msgExpectedInstallStateField), state_start);
return parser.extract_messages().error.value_or_exit(VCPKG_LINE_INFO);
}
if (parser.messages().good())
{
return result;
}
}
return parser.extract_messages().error.value_or_exit(VCPKG_LINE_INFO);
}
void serialize(const StatusParagraph& pgh, std::string& out)
{
serialize(pgh.package, out);
append_paragraph_field(ParagraphIdStatus, pgh.status.to_string(), out);
} }
StatusParagraph::StatusParagraph(StringView origin, Paragraph&& fields) StatusParagraph::StatusParagraph(StringView origin, Paragraph&& fields)
: want(Want::ERROR_STATE), state(InstallState::ERROR_STATE)
{ {
auto status_it = fields.find(ParagraphIdStatus); auto status_it = fields.find(ParagraphIdStatus);
Checks::msg_check_maybe_upgrade(VCPKG_LINE_INFO, status_it != fields.end(), msgExpectedStatusField); Checks::msg_check_maybe_upgrade(VCPKG_LINE_INFO, status_it != fields.end(), msgExpectedStatusField);
std::string status_field = std::move(status_it->second.first); auto status_field = std::move(status_it->second);
fields.erase(status_it); fields.erase(status_it);
this->package = BinaryParagraph(origin, std::move(fields)); this->package = BinaryParagraph(origin, std::move(fields));
this->status =
auto b = status_field.begin(); parse_status_line(status_field.first, origin, status_field.second).value_or_exit(VCPKG_LINE_INFO);
const auto mark = b;
const auto e = status_field.end();
// Todo: improve error handling
while (b != e && *b != ' ')
++b;
want = [](const std::string& text) {
if (text == StatusUnknown) return Want::UNKNOWN;
if (text == StatusInstall) return Want::INSTALL;
if (text == StatusHold) return Want::HOLD;
if (text == StatusDeinstall) return Want::DEINSTALL;
if (text == StatusPurge) return Want::PURGE;
return Want::ERROR_STATE;
}(std::string(mark, b));
if (std::distance(b, e) < 4) return;
b += 4;
state = [](const std::string& text) {
if (text == StatusNotInstalled) return InstallState::NOT_INSTALLED;
if (text == StatusInstalled) return InstallState::INSTALLED;
if (text == StatusHalfInstalled) return InstallState::HALF_INSTALLED;
return InstallState::ERROR_STATE;
}(std::string(b, e));
} }
StringLiteral to_string_literal(InstallState f) StringLiteral to_string_literal(InstallState f)
@ -76,7 +110,6 @@ namespace vcpkg
case Want::HOLD: return StatusHold; case Want::HOLD: return StatusHold;
case Want::INSTALL: return StatusInstall; case Want::INSTALL: return StatusInstall;
case Want::PURGE: return StatusPurge; case Want::PURGE: return StatusPurge;
case Want::UNKNOWN: return StatusUnknown;
default: Checks::unreachable(VCPKG_LINE_INFO); default: Checks::unreachable(VCPKG_LINE_INFO);
} }
} }

Просмотреть файл

@ -19,11 +19,16 @@ namespace vcpkg
if (p->package.spec.name() == name && p->package.spec.triplet() == triplet) if (p->package.spec.name() == name && p->package.spec.triplet() == triplet)
{ {
if (p->package.is_feature()) if (p->package.is_feature())
{
spghs.emplace_back(&p); spghs.emplace_back(&p);
}
else else
{
spghs.emplace(spghs.begin(), &p); spghs.emplace(spghs.begin(), &p);
}
} }
} }
return spghs; return spghs;
} }
@ -46,10 +51,13 @@ namespace vcpkg
} }
} }
} }
if (ipv.core != nullptr) if (ipv.core != nullptr)
{
return ipv; return ipv;
else }
return nullopt;
return nullopt;
} }
StatusParagraphs::iterator StatusParagraphs::find(const std::string& name, StatusParagraphs::iterator StatusParagraphs::find(const std::string& name,
@ -76,6 +84,7 @@ namespace vcpkg
// The core feature maps to .feature == "" // The core feature maps to .feature == ""
return find(name, triplet, ""); return find(name, triplet, "");
} }
return std::find_if(begin(), end(), [&](const std::unique_ptr<StatusParagraph>& pgh) { return std::find_if(begin(), end(), [&](const std::unique_ptr<StatusParagraph>& pgh) {
const PackageSpec& spec = pgh->package.spec; const PackageSpec& spec = pgh->package.spec;
return spec.name() == name && spec.triplet() == triplet && pgh->package.feature == feature; return spec.name() == name && spec.triplet() == triplet && pgh->package.feature == feature;
@ -89,10 +98,8 @@ namespace vcpkg
{ {
return it; return it;
} }
else
{ return end();
return end();
}
} }
StatusParagraphs::const_iterator StatusParagraphs::find_installed(const FeatureSpec& spec) const StatusParagraphs::const_iterator StatusParagraphs::find_installed(const FeatureSpec& spec) const
@ -102,10 +109,8 @@ namespace vcpkg
{ {
return it; return it;
} }
else
{ return end();
return end();
}
} }
bool vcpkg::StatusParagraphs::is_installed(const PackageSpec& spec) const bool vcpkg::StatusParagraphs::is_installed(const PackageSpec& spec) const