[vcpkg] Track parser row/col state in Paragraph (renamed from RawParagraph) (#9987)

This commit is contained in:
Robert Schumacher 2020-02-09 14:50:26 -08:00 committed by GitHub
parent 039098c954
commit a33044c186
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 271 additions and 202 deletions

View File

@ -23,6 +23,18 @@ namespace vcpkg::Test
const std::vector<std::pair<const char*, const char*>>& features = {},
const std::vector<const char*>& default_features = {});
inline auto test_parse_control_file(const std::vector<std::unordered_map<std::string, std::string>>& v)
{
std::vector<vcpkg::Parse::Paragraph> pghs;
for (auto&& p : v)
{
pghs.emplace_back();
for (auto&& kv : p)
pghs.back().emplace(kv.first, std::make_pair(kv.second, vcpkg::Parse::TextRowCol{}));
}
return vcpkg::SourceControlFile::parse_control_file("", std::move(pghs));
}
std::unique_ptr<vcpkg::StatusParagraph> make_status_pgh(const char* name,
const char* depends = "",
const char* default_features = "",

View File

@ -12,7 +12,7 @@ namespace vcpkg
struct BinaryParagraph
{
BinaryParagraph();
explicit BinaryParagraph(Parse::RawParagraph fields);
explicit BinaryParagraph(Parse::Paragraph fields);
BinaryParagraph(const SourceParagraph& spgh,
Triplet triplet,
const std::string& abi_tag,

View File

@ -2,6 +2,7 @@
#include <vcpkg/base/expected.h>
#include <vcpkg/packagespec.h>
#include <vcpkg/textrowcol.h>
#include <memory>
#include <string>
@ -21,25 +22,30 @@ namespace vcpkg::Parse
template<class P>
using ParseExpected = vcpkg::ExpectedT<std::unique_ptr<P>, std::unique_ptr<ParseControlErrorInfo>>;
using RawParagraph = std::unordered_map<std::string, std::string>;
using Paragraph = std::unordered_map<std::string, std::pair<std::string, TextRowCol>>;
struct ParagraphParser
{
ParagraphParser(RawParagraph&& fields) : fields(std::move(fields)) {}
ParagraphParser(Paragraph&& fields) : fields(std::move(fields)) {}
void required_field(const std::string& fieldname, std::string& out);
std::string optional_field(const std::string& fieldname) const;
std::string optional_field(const std::string& fieldname);
void required_field(const std::string& fieldname, std::pair<std::string&, TextRowCol&> out);
void optional_field(const std::string& fieldname, std::pair<std::string&, TextRowCol&> out);
std::unique_ptr<ParseControlErrorInfo> error_info(const std::string& name) const;
private:
RawParagraph&& fields;
Paragraph&& fields;
std::vector<std::string> missing_fields;
};
ExpectedS<std::vector<std::string>> parse_default_features_list(const std::string& str,
CStringView origin = "<unknown>");
CStringView origin = "<unknown>",
TextRowCol textrowcol = {});
ExpectedS<std::vector<ParsedQualifiedSpecifier>> parse_qualified_specifier_list(const std::string& str,
CStringView origin = "<unknown>");
CStringView origin = "<unknown>",
TextRowCol textrowcol = {});
ExpectedS<std::vector<Dependency>> parse_dependencies_list(const std::string& str,
CStringView origin = "<unknown>");
CStringView origin = "<unknown>",
TextRowCol textrowcol = {});
}

View File

@ -8,11 +8,11 @@
namespace vcpkg::Paragraphs
{
using RawParagraph = Parse::RawParagraph;
using Paragraph = Parse::Paragraph;
ExpectedS<RawParagraph> get_single_paragraph(const Files::Filesystem& fs, const fs::path& control_path);
ExpectedS<std::vector<RawParagraph>> get_paragraphs(const Files::Filesystem& fs, const fs::path& control_path);
ExpectedS<std::vector<RawParagraph>> parse_paragraphs(const std::string& str, const std::string& origin);
ExpectedS<Paragraph> get_single_paragraph(const Files::Filesystem& fs, const fs::path& control_path);
ExpectedS<std::vector<Paragraph>> get_paragraphs(const Files::Filesystem& fs, const fs::path& control_path);
ExpectedS<std::vector<Paragraph>> parse_paragraphs(const std::string& str, const std::string& origin);
Parse::ParseExpected<SourceControlFile> try_load_port(const Files::Filesystem& fs, const fs::path& control_path);

View File

@ -18,14 +18,20 @@ namespace vcpkg::Parse
struct ParseError : IParseError
{
ParseError(std::string origin, int row, int column, std::string line, std::string message)
: origin(std::move(origin)), row(row), column(column), line(std::move(line)), message(std::move(message))
ParseError(std::string origin, int row, int column, int caret_col, std::string line, std::string message)
: origin(std::move(origin))
, row(row)
, column(column)
, caret_col(caret_col)
, line(std::move(line))
, message(std::move(message))
{
}
const std::string origin;
const int row;
const int column;
const int caret_col;
const std::string line;
const std::string message;
@ -105,27 +111,8 @@ namespace vcpkg::Parse
const char* it() const { return m_it; }
char cur() const { return *m_it; }
SourceLoc cur_loc() const { return {m_it, row, column}; }
char next()
{
char ch = *m_it;
// See https://www.gnu.org/prep/standards/standards.html#Errors
if (ch == '\t')
column = (column + 7) / 8 * 8 + 1; // round to next 8-width tab stop
else if (ch == '\n')
{
row++;
column = 1;
}
else if (ch == '\0')
{
return '\0';
}
else
{
++column;
}
return *++m_it;
}
TextRowCol cur_rowcol() const { return {row, column}; }
char next();
bool at_eof() const { return *m_it == 0; }
void add_error(std::string message) { add_error(std::move(message), cur_loc()); }

View File

@ -70,7 +70,7 @@ namespace vcpkg
}
static Parse::ParseExpected<SourceControlFile> parse_control_file(
const fs::path& path_to_control, std::vector<Parse::RawParagraph>&& control_paragraphs);
const fs::path& path_to_control, std::vector<Parse::Paragraph>&& control_paragraphs);
std::unique_ptr<SourceParagraph> core_paragraph;
std::vector<std::unique_ptr<FeatureParagraph>> feature_paragraphs;

View File

@ -30,7 +30,7 @@ namespace vcpkg
struct StatusParagraph
{
StatusParagraph() noexcept;
explicit StatusParagraph(Parse::RawParagraph&& fields);
explicit StatusParagraph(Parse::Paragraph&& fields);
bool is_installed() const { return want == Want::INSTALL && state == InstallState::INSTALLED; }

View File

@ -6,15 +6,35 @@
#include <vcpkg/paragraphs.h>
namespace Strings = vcpkg::Strings;
using vcpkg::Parse::Paragraph;
auto test_parse_control_file(const std::vector<std::unordered_map<std::string, std::string>>& v)
{
std::vector<Paragraph> pghs;
for (auto&& p : v)
{
pghs.emplace_back();
for (auto&& kv : p)
pghs.back().emplace(kv.first, std::make_pair(kv.second, vcpkg::Parse::TextRowCol{}));
}
return vcpkg::SourceControlFile::parse_control_file("", std::move(pghs));
}
auto test_make_binary_paragraph(const std::unordered_map<std::string, std::string>& v)
{
Paragraph pgh;
for (auto&& kv : v)
pgh.emplace(kv.first, std::make_pair(kv.second, vcpkg::Parse::TextRowCol{}));
return vcpkg::BinaryParagraph(std::move(pgh));
}
TEST_CASE ("SourceParagraph construct minimum", "[paragraph]")
{
auto m_pgh =
vcpkg::SourceControlFile::parse_control_file("",
std::vector<std::unordered_map<std::string, std::string>>{{
{"Source", "zlib"},
{"Version", "1.2.8"},
}});
auto m_pgh = test_parse_control_file({{
{"Source", "zlib"},
{"Version", "1.2.8"},
}});
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
@ -28,16 +48,14 @@ TEST_CASE ("SourceParagraph construct minimum", "[paragraph]")
TEST_CASE ("SourceParagraph construct maximum", "[paragraph]")
{
auto m_pgh =
vcpkg::SourceControlFile::parse_control_file("",
std::vector<std::unordered_map<std::string, std::string>>{{
{"Source", "s"},
{"Version", "v"},
{"Maintainer", "m"},
{"Description", "d"},
{"Build-Depends", "bd"},
{"Default-Features", "df"},
}});
auto m_pgh = test_parse_control_file({{
{"Source", "s"},
{"Version", "v"},
{"Maintainer", "m"},
{"Description", "d"},
{"Build-Depends", "bd"},
{"Default-Features", "df"},
}});
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
@ -53,13 +71,11 @@ TEST_CASE ("SourceParagraph construct maximum", "[paragraph]")
TEST_CASE ("SourceParagraph two depends", "[paragraph]")
{
auto m_pgh =
vcpkg::SourceControlFile::parse_control_file("",
std::vector<std::unordered_map<std::string, std::string>>{{
{"Source", "zlib"},
{"Version", "1.2.8"},
{"Build-Depends", "z, openssl"},
}});
auto m_pgh = test_parse_control_file({{
{"Source", "zlib"},
{"Version", "1.2.8"},
{"Build-Depends", "z, openssl"},
}});
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
@ -70,13 +86,11 @@ TEST_CASE ("SourceParagraph two depends", "[paragraph]")
TEST_CASE ("SourceParagraph three depends", "[paragraph]")
{
auto m_pgh =
vcpkg::SourceControlFile::parse_control_file("",
std::vector<std::unordered_map<std::string, std::string>>{{
{"Source", "zlib"},
{"Version", "1.2.8"},
{"Build-Depends", "z, openssl, xyz"},
}});
auto m_pgh = test_parse_control_file({{
{"Source", "zlib"},
{"Version", "1.2.8"},
{"Build-Depends", "z, openssl, xyz"},
}});
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
@ -88,13 +102,11 @@ TEST_CASE ("SourceParagraph three depends", "[paragraph]")
TEST_CASE ("SourceParagraph construct qualified depends", "[paragraph]")
{
auto m_pgh =
vcpkg::SourceControlFile::parse_control_file("",
std::vector<std::unordered_map<std::string, std::string>>{{
{"Source", "zlib"},
{"Version", "1.2.8"},
{"Build-Depends", "liba (windows), libb (uwp)"},
}});
auto m_pgh = test_parse_control_file({{
{"Source", "zlib"},
{"Version", "1.2.8"},
{"Build-Depends", "liba (windows), libb (uwp)"},
}});
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
@ -111,13 +123,11 @@ TEST_CASE ("SourceParagraph construct qualified depends", "[paragraph]")
TEST_CASE ("SourceParagraph default features", "[paragraph]")
{
auto m_pgh =
vcpkg::SourceControlFile::parse_control_file("",
std::vector<std::unordered_map<std::string, std::string>>{{
{"Source", "a"},
{"Version", "1.0"},
{"Default-Features", "a1"},
}});
auto m_pgh = test_parse_control_file({{
{"Source", "a"},
{"Version", "1.0"},
{"Default-Features", "a1"},
}});
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
@ -127,7 +137,7 @@ TEST_CASE ("SourceParagraph default features", "[paragraph]")
TEST_CASE ("BinaryParagraph construct minimum", "[paragraph]")
{
vcpkg::BinaryParagraph pgh({
auto pgh = test_make_binary_paragraph({
{"Package", "zlib"},
{"Version", "1.2.8"},
{"Architecture", "x86-windows"},
@ -144,7 +154,7 @@ TEST_CASE ("BinaryParagraph construct minimum", "[paragraph]")
TEST_CASE ("BinaryParagraph construct maximum", "[paragraph]")
{
vcpkg::BinaryParagraph pgh({
auto pgh = test_make_binary_paragraph({
{"Package", "s"},
{"Version", "v"},
{"Architecture", "x86-windows"},
@ -164,7 +174,7 @@ TEST_CASE ("BinaryParagraph construct maximum", "[paragraph]")
TEST_CASE ("BinaryParagraph three depends", "[paragraph]")
{
vcpkg::BinaryParagraph pgh({
auto pgh = test_make_binary_paragraph({
{"Package", "zlib"},
{"Version", "1.2.8"},
{"Architecture", "x86-windows"},
@ -180,7 +190,7 @@ TEST_CASE ("BinaryParagraph three depends", "[paragraph]")
TEST_CASE ("BinaryParagraph abi", "[paragraph]")
{
vcpkg::BinaryParagraph pgh({
auto pgh = test_make_binary_paragraph({
{"Package", "zlib"},
{"Version", "1.2.8"},
{"Architecture", "x86-windows"},
@ -194,7 +204,7 @@ TEST_CASE ("BinaryParagraph abi", "[paragraph]")
TEST_CASE ("BinaryParagraph default features", "[paragraph]")
{
vcpkg::BinaryParagraph pgh({
auto pgh = test_make_binary_paragraph({
{"Package", "a"},
{"Version", "1.0"},
{"Architecture", "x86-windows"},
@ -220,7 +230,7 @@ TEST_CASE ("parse paragraphs one field", "[paragraph]")
auto pghs = vcpkg::Paragraphs::parse_paragraphs(str, "").value_or_exit(VCPKG_LINE_INFO);
REQUIRE(pghs.size() == 1);
REQUIRE(pghs[0].size() == 1);
REQUIRE(pghs[0]["f1"] == "v1");
REQUIRE(pghs[0]["f1"].first == "v1");
}
TEST_CASE ("parse paragraphs one pgh", "[paragraph]")
@ -230,8 +240,8 @@ TEST_CASE ("parse paragraphs one pgh", "[paragraph]")
auto pghs = vcpkg::Paragraphs::parse_paragraphs(str, "").value_or_exit(VCPKG_LINE_INFO);
REQUIRE(pghs.size() == 1);
REQUIRE(pghs[0].size() == 2);
REQUIRE(pghs[0]["f1"] == "v1");
REQUIRE(pghs[0]["f2"] == "v2");
REQUIRE(pghs[0]["f1"].first == "v1");
REQUIRE(pghs[0]["f2"].first == "v2");
}
TEST_CASE ("parse paragraphs two pgh", "[paragraph]")
@ -245,11 +255,11 @@ TEST_CASE ("parse paragraphs two pgh", "[paragraph]")
REQUIRE(pghs.size() == 2);
REQUIRE(pghs[0].size() == 2);
REQUIRE(pghs[0]["f1"] == "v1");
REQUIRE(pghs[0]["f2"] == "v2");
REQUIRE(pghs[0]["f1"].first == "v1");
REQUIRE(pghs[0]["f2"].first == "v2");
REQUIRE(pghs[1].size() == 2);
REQUIRE(pghs[1]["f3"] == "v3");
REQUIRE(pghs[1]["f4"] == "v4");
REQUIRE(pghs[1]["f3"].first == "v3");
REQUIRE(pghs[1]["f4"].first == "v4");
}
TEST_CASE ("parse paragraphs field names", "[paragraph]")
@ -286,8 +296,8 @@ TEST_CASE ("parse paragraphs empty fields", "[paragraph]")
REQUIRE(pghs.size() == 1);
REQUIRE(pghs[0].size() == 2);
REQUIRE(pghs[0]["f1"] == "");
REQUIRE(pghs[0]["f2"] == "");
REQUIRE(pghs[0]["f1"].first == "");
REQUIRE(pghs[0]["f2"].first == "");
REQUIRE(pghs[0].size() == 2);
}
@ -301,8 +311,8 @@ TEST_CASE ("parse paragraphs multiline fields", "[paragraph]")
auto pghs = vcpkg::Paragraphs::parse_paragraphs(str, "").value_or_exit(VCPKG_LINE_INFO);
REQUIRE(pghs.size() == 1);
REQUIRE(pghs[0]["f1"] == "simple\n f1");
REQUIRE(pghs[0]["f2"] == "\n f2\n continue");
REQUIRE(pghs[0]["f1"].first == "simple\n f1");
REQUIRE(pghs[0]["f2"].first == "\n f2\n continue");
}
TEST_CASE ("parse paragraphs crlfs", "[paragraph]")
@ -316,11 +326,11 @@ TEST_CASE ("parse paragraphs crlfs", "[paragraph]")
REQUIRE(pghs.size() == 2);
REQUIRE(pghs[0].size() == 2);
REQUIRE(pghs[0]["f1"] == "v1");
REQUIRE(pghs[0]["f2"] == "v2");
REQUIRE(pghs[0]["f1"].first == "v1");
REQUIRE(pghs[0]["f2"].first == "v2");
REQUIRE(pghs[1].size() == 2);
REQUIRE(pghs[1]["f3"] == "v3");
REQUIRE(pghs[1]["f4"] == "v4");
REQUIRE(pghs[1]["f3"].first == "v3");
REQUIRE(pghs[1]["f4"].first == "v4");
}
TEST_CASE ("parse paragraphs comment", "[paragraph]")
@ -338,11 +348,11 @@ TEST_CASE ("parse paragraphs comment", "[paragraph]")
REQUIRE(pghs.size() == 2);
REQUIRE(pghs[0].size() == 2);
REQUIRE(pghs[0]["f1"] == "v1");
REQUIRE(pghs[0]["f2"] == "v2");
REQUIRE(pghs[0]["f1"].first == "v1");
REQUIRE(pghs[0]["f2"].first == "v2");
REQUIRE(pghs[1].size());
REQUIRE(pghs[1]["f3"] == "v3");
REQUIRE(pghs[1]["f4"] == "v4");
REQUIRE(pghs[1]["f3"].first == "v3");
REQUIRE(pghs[1]["f4"].first == "v4");
}
TEST_CASE ("parse comment before single line feed", "[paragraph]")
@ -351,12 +361,12 @@ TEST_CASE ("parse comment before single line feed", "[paragraph]")
"#comment\n";
auto pghs = vcpkg::Paragraphs::parse_paragraphs(str, "").value_or_exit(VCPKG_LINE_INFO);
REQUIRE(pghs[0].size() == 1);
REQUIRE(pghs[0]["f1"] == "v1");
REQUIRE(pghs[0]["f1"].first == "v1");
}
TEST_CASE ("BinaryParagraph serialize min", "[paragraph]")
{
vcpkg::BinaryParagraph pgh({
auto pgh = test_make_binary_paragraph({
{"Package", "zlib"},
{"Version", "1.2.8"},
{"Architecture", "x86-windows"},
@ -367,16 +377,16 @@ TEST_CASE ("BinaryParagraph serialize min", "[paragraph]")
REQUIRE(pghs.size() == 1);
REQUIRE(pghs[0].size() == 5);
REQUIRE(pghs[0]["Package"] == "zlib");
REQUIRE(pghs[0]["Version"] == "1.2.8");
REQUIRE(pghs[0]["Architecture"] == "x86-windows");
REQUIRE(pghs[0]["Multi-Arch"] == "same");
REQUIRE(pghs[0]["Type"] == "Port");
REQUIRE(pghs[0]["Package"].first == "zlib");
REQUIRE(pghs[0]["Version"].first == "1.2.8");
REQUIRE(pghs[0]["Architecture"].first == "x86-windows");
REQUIRE(pghs[0]["Multi-Arch"].first == "same");
REQUIRE(pghs[0]["Type"].first == "Port");
}
TEST_CASE ("BinaryParagraph serialize max", "[paragraph]")
{
vcpkg::BinaryParagraph pgh({
auto pgh = test_make_binary_paragraph({
{"Package", "zlib"},
{"Version", "1.2.8"},
{"Architecture", "x86-windows"},
@ -390,18 +400,18 @@ TEST_CASE ("BinaryParagraph serialize max", "[paragraph]")
REQUIRE(pghs.size() == 1);
REQUIRE(pghs[0].size() == 8);
REQUIRE(pghs[0]["Package"] == "zlib");
REQUIRE(pghs[0]["Version"] == "1.2.8");
REQUIRE(pghs[0]["Architecture"] == "x86-windows");
REQUIRE(pghs[0]["Multi-Arch"] == "same");
REQUIRE(pghs[0]["Description"] == "first line\n second line");
REQUIRE(pghs[0]["Depends"] == "dep");
REQUIRE(pghs[0]["Type"] == "Port");
REQUIRE(pghs[0]["Package"].first == "zlib");
REQUIRE(pghs[0]["Version"].first == "1.2.8");
REQUIRE(pghs[0]["Architecture"].first == "x86-windows");
REQUIRE(pghs[0]["Multi-Arch"].first == "same");
REQUIRE(pghs[0]["Description"].first == "first line\n second line");
REQUIRE(pghs[0]["Depends"].first == "dep");
REQUIRE(pghs[0]["Type"].first == "Port");
}
TEST_CASE ("BinaryParagraph serialize multiple deps", "[paragraph]")
{
vcpkg::BinaryParagraph pgh({
auto pgh = test_make_binary_paragraph({
{"Package", "zlib"},
{"Version", "1.2.8"},
{"Architecture", "x86-windows"},
@ -412,12 +422,12 @@ TEST_CASE ("BinaryParagraph serialize multiple deps", "[paragraph]")
auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss, "").value_or_exit(VCPKG_LINE_INFO);
REQUIRE(pghs.size() == 1);
REQUIRE(pghs[0]["Depends"] == "a, b, c");
REQUIRE(pghs[0]["Depends"].first == "a, b, c");
}
TEST_CASE ("BinaryParagraph serialize abi", "[paragraph]")
{
vcpkg::BinaryParagraph pgh({
auto pgh = test_make_binary_paragraph({
{"Package", "zlib"},
{"Version", "1.2.8"},
{"Architecture", "x86-windows"},
@ -429,5 +439,5 @@ TEST_CASE ("BinaryParagraph serialize abi", "[paragraph]")
auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss, "").value_or_exit(VCPKG_LINE_INFO);
REQUIRE(pghs.size() == 1);
REQUIRE(pghs[0]["Abi"] == "123abc");
REQUIRE(pghs[0]["Abi"].first == "123abc");
}

View File

@ -24,7 +24,7 @@ Status: install ok installed
REQUIRE(pghs);
StatusParagraphs status_db(
Util::fmap(*pghs.get(), [](RawParagraph& rpgh) { return std::make_unique<StatusParagraph>(std::move(rpgh)); }));
Util::fmap(*pghs.get(), [](Paragraph& rpgh) { return std::make_unique<StatusParagraph>(std::move(rpgh)); }));
auto it = status_db.find_installed({"ffmpeg", Triplet::X64_WINDOWS});
REQUIRE(it != status_db.end());
@ -45,7 +45,7 @@ Status: purge ok not-installed
REQUIRE(pghs);
StatusParagraphs status_db(
Util::fmap(*pghs.get(), [](RawParagraph& rpgh) { return std::make_unique<StatusParagraph>(std::move(rpgh)); }));
Util::fmap(*pghs.get(), [](Paragraph& rpgh) { return std::make_unique<StatusParagraph>(std::move(rpgh)); }));
auto it = status_db.find_installed({"ffmpeg", Triplet::X64_WINDOWS});
REQUIRE(it == status_db.end());
@ -74,7 +74,7 @@ Status: purge ok not-installed
REQUIRE(pghs);
StatusParagraphs status_db(
Util::fmap(*pghs.get(), [](RawParagraph& rpgh) { return std::make_unique<StatusParagraph>(std::move(rpgh)); }));
Util::fmap(*pghs.get(), [](Paragraph& rpgh) { return std::make_unique<StatusParagraph>(std::move(rpgh)); }));
auto it = status_db.find_installed({"ffmpeg", Triplet::X64_WINDOWS});
REQUIRE(it != status_db.end());
@ -106,7 +106,7 @@ Status: install ok installed
REQUIRE(pghs);
StatusParagraphs status_db(
Util::fmap(*pghs.get(), [](RawParagraph& rpgh) { return std::make_unique<StatusParagraph>(std::move(rpgh)); }));
Util::fmap(*pghs.get(), [](Paragraph& rpgh) { return std::make_unique<StatusParagraph>(std::move(rpgh)); }));
// Feature "openssl" is installed and should therefore be found
auto it = status_db.find_installed({{"ffmpeg", Triplet::X64_WINDOWS}, "openssl"});

View File

@ -20,7 +20,7 @@ TEST_CASE ("find outdated packages basic", "[update]")
StatusParagraphs status_db(std::move(status_paragraphs));
std::unordered_map<std::string, SourceControlFileLocation> map;
auto scf = unwrap(SourceControlFile::parse_control_file("", Pgh{{{"Source", "a"}, {"Version", "0"}}}));
auto scf = unwrap(test_parse_control_file({{{"Source", "a"}, {"Version", "0"}}}));
map.emplace("a", SourceControlFileLocation{std::move(scf), ""});
PortFileProvider::MapPortFileProvider provider(map);
@ -44,7 +44,7 @@ TEST_CASE ("find outdated packages features", "[update]")
StatusParagraphs status_db(std::move(status_paragraphs));
std::unordered_map<std::string, SourceControlFileLocation> map;
auto scf = unwrap(SourceControlFile::parse_control_file("", Pgh{{{"Source", "a"}, {"Version", "0"}}}));
auto scf = unwrap(test_parse_control_file({{{"Source", "a"}, {"Version", "0"}}}));
map.emplace("a", SourceControlFileLocation{std::move(scf), ""});
PortFileProvider::MapPortFileProvider provider(map);
@ -70,7 +70,7 @@ TEST_CASE ("find outdated packages features 2", "[update]")
StatusParagraphs status_db(std::move(status_paragraphs));
std::unordered_map<std::string, SourceControlFileLocation> map;
auto scf = unwrap(SourceControlFile::parse_control_file("", Pgh{{{"Source", "a"}, {"Version", "0"}}}));
auto scf = unwrap(test_parse_control_file({{{"Source", "a"}, {"Version", "0"}}}));
map.emplace("a", SourceControlFileLocation{std::move(scf), ""});
PortFileProvider::MapPortFileProvider provider(map);
@ -91,7 +91,7 @@ TEST_CASE ("find outdated packages none", "[update]")
StatusParagraphs status_db(std::move(status_paragraphs));
std::unordered_map<std::string, SourceControlFileLocation> map;
auto scf = unwrap(SourceControlFile::parse_control_file("", Pgh{{{"Source", "a"}, {"Version", "2"}}}));
auto scf = unwrap(test_parse_control_file({{{"Source", "a"}, {"Version", "2"}}}));
map.emplace("a", SourceControlFileLocation{std::move(scf), ""});
PortFileProvider::MapPortFileProvider provider(map);

View File

@ -58,7 +58,7 @@ namespace vcpkg::Test
{"Build-Depends", feature.second},
});
}
auto m_pgh = vcpkg::SourceControlFile::parse_control_file("", std::move(scf_pghs));
auto m_pgh = test_parse_control_file(std::move(scf_pghs));
REQUIRE(m_pgh.has_value());
return std::move(*m_pgh.get());
}
@ -68,14 +68,13 @@ namespace vcpkg::Test
const char* default_features,
const char* triplet)
{
using Pgh = std::unordered_map<std::string, std::string>;
return std::make_unique<StatusParagraph>(Pgh{{"Package", name},
{"Version", "1"},
{"Architecture", triplet},
{"Multi-Arch", "same"},
{"Depends", depends},
{"Default-Features", default_features},
{"Status", "install ok installed"}});
return std::make_unique<StatusParagraph>(Parse::Paragraph{{"Package", {name, {}}},
{"Version", {"1", {}}},
{"Architecture", {triplet, {}}},
{"Multi-Arch", {"same", {}}},
{"Depends", {depends, {}}},
{"Default-Features", {default_features, {}}},
{"Status", {"install ok installed", {}}}});
}
std::unique_ptr<StatusParagraph> make_status_feature_pgh(const char* name,
@ -83,14 +82,12 @@ namespace vcpkg::Test
const char* depends,
const char* triplet)
{
using Pgh = std::unordered_map<std::string, std::string>;
return std::make_unique<StatusParagraph>(Pgh{{"Package", name},
{"Version", "1"},
{"Feature", feature},
{"Architecture", triplet},
{"Multi-Arch", "same"},
{"Depends", depends},
{"Status", "install ok installed"}});
return std::make_unique<StatusParagraph>(Parse::Paragraph{{"Package", {name, {}}},
{"Feature", {feature, {}}},
{"Architecture", {triplet, {}}},
{"Multi-Arch", {"same", {}}},
{"Depends", {depends, {}}},
{"Status", {"install ok installed", {}}}});
}
PackageSpec PackageSpecMap::emplace(const char* name,

View File

@ -30,7 +30,7 @@ namespace vcpkg
BinaryParagraph::BinaryParagraph() = default;
BinaryParagraph::BinaryParagraph(Parse::RawParagraph fields)
BinaryParagraph::BinaryParagraph(Parse::Paragraph fields)
{
using namespace vcpkg::Parse;

View File

@ -1011,7 +1011,7 @@ namespace vcpkg::Build
Commands::Version::version());
}
static BuildInfo inner_create_buildinfo(Parse::RawParagraph pgh)
static BuildInfo inner_create_buildinfo(Parse::Paragraph pgh)
{
Parse::ParagraphParser parser(std::move(pgh));
@ -1068,7 +1068,7 @@ namespace vcpkg::Build
BuildInfo read_build_info(const Files::Filesystem& fs, const fs::path& filepath)
{
const ExpectedS<Parse::RawParagraph> pghs = Paragraphs::get_single_paragraph(fs, filepath);
const ExpectedS<Parse::Paragraph> pghs = Paragraphs::get_single_paragraph(fs, filepath);
Checks::check_exit(
VCPKG_LINE_INFO, pghs.get() != nullptr, "Invalid BUILD_INFO file for package: %s", pghs.error());
return inner_create_buildinfo(*pghs.get());

View File

@ -224,12 +224,10 @@ namespace vcpkg::Commands::CI
};
static bool supported_for_triplet(const CMakeVars::TripletCMakeVarProvider& var_provider,
const InstallPlanAction* install_plan)
{
const std::string& supports_expression =
install_plan->source_control_file_location.value_or_exit(VCPKG_LINE_INFO)
.source_control_file->core_paragraph->supports_expression;
auto&& scfl = install_plan->source_control_file_location.value_or_exit(VCPKG_LINE_INFO);
const std::string& supports_expression = scfl.source_control_file->core_paragraph->supports_expression;
if (supports_expression.empty())
{
return true; // default to 'supported'

View File

@ -40,7 +40,7 @@ namespace vcpkg::Paragraphs
if (fieldname.empty()) return add_error("expected fieldname");
}
void get_paragraph(RawParagraph& fields)
void get_paragraph(Paragraph& fields)
{
fields.clear();
std::string fieldname;
@ -59,17 +59,17 @@ namespace vcpkg::Paragraphs
if (Util::Sets::contains(fields, fieldname)) return add_error("duplicate field", loc);
next();
skip_tabs_spaces();
auto rowcol = cur_rowcol();
get_fieldvalue(fieldvalue);
fields.emplace(fieldname, fieldvalue);
fields.emplace(fieldname, std::make_pair(fieldvalue, rowcol));
} while (!is_lineend(cur()));
}
public:
ExpectedS<std::vector<RawParagraph>> get_paragraphs(CStringView text, CStringView origin)
ExpectedS<std::vector<Paragraph>> get_paragraphs(CStringView text, CStringView origin)
{
std::vector<RawParagraph> paragraphs;
std::vector<Paragraph> paragraphs;
init(text, origin);
@ -86,7 +86,7 @@ namespace vcpkg::Paragraphs
}
};
static ExpectedS<RawParagraph> parse_single_paragraph(const std::string& str, const std::string& origin)
static ExpectedS<Paragraph> parse_single_paragraph(const std::string& str, const std::string& origin)
{
PghParser parser;
auto pghs = parser.get_paragraphs(str, origin);
@ -102,7 +102,7 @@ namespace vcpkg::Paragraphs
}
}
ExpectedS<RawParagraph> get_single_paragraph(const Files::Filesystem& fs, const fs::path& control_path)
ExpectedS<Paragraph> get_single_paragraph(const Files::Filesystem& fs, const fs::path& control_path)
{
const Expected<std::string> contents = fs.read_contents(control_path);
if (auto spgh = contents.get())
@ -113,7 +113,7 @@ namespace vcpkg::Paragraphs
return contents.error().message();
}
ExpectedS<std::vector<RawParagraph>> get_paragraphs(const Files::Filesystem& fs, const fs::path& control_path)
ExpectedS<std::vector<Paragraph>> get_paragraphs(const Files::Filesystem& fs, const fs::path& control_path)
{
const Expected<std::string> contents = fs.read_contents(control_path);
if (auto spgh = contents.get())
@ -124,7 +124,7 @@ namespace vcpkg::Paragraphs
return contents.error().message();
}
ExpectedS<std::vector<RawParagraph>> parse_paragraphs(const std::string& str, const std::string& origin)
ExpectedS<std::vector<Paragraph>> parse_paragraphs(const std::string& str, const std::string& origin)
{
PghParser parser;
return parser.get_paragraphs(str, origin);
@ -133,7 +133,7 @@ namespace vcpkg::Paragraphs
ParseExpected<SourceControlFile> try_load_port(const Files::Filesystem& fs, const fs::path& path)
{
const auto path_to_control = path / "CONTROL";
ExpectedS<std::vector<RawParagraph>> pghs = get_paragraphs(fs, path_to_control);
ExpectedS<std::vector<Paragraph>> pghs = get_paragraphs(fs, path_to_control);
if (auto vector_pghs = pghs.get())
{
return SourceControlFile::parse_control_file(path_to_control, std::move(*vector_pghs));
@ -146,7 +146,7 @@ namespace vcpkg::Paragraphs
ExpectedS<BinaryControlFile> try_load_cached_package(const VcpkgPaths& paths, const PackageSpec& spec)
{
ExpectedS<std::vector<RawParagraph>> pghs =
ExpectedS<std::vector<Paragraph>> pghs =
get_paragraphs(paths.get_filesystem(), paths.package_dir(spec) / "CONTROL");
if (auto p = pghs.get())

View File

@ -11,8 +11,28 @@ using namespace vcpkg;
namespace vcpkg::Parse
{
static void advance_rowcol(char ch, int& row, int& column)
{
if (ch == '\t')
column = (column + 7) / 8 * 8 + 1; // round to next 8-width tab stop
else if (ch == '\n')
{
row++;
column = 1;
}
else
{
++column;
}
}
std::string ParseError::format() const
{
int ignore_row = 1;
int spacing = 20;
for (int i = 0; i < caret_col; ++i)
advance_rowcol(line[i], ignore_row, spacing);
return Strings::concat("Error: ",
origin,
":",
@ -22,14 +42,26 @@ namespace vcpkg::Parse
": ",
message,
"\n"
" on expression: \"",
" on expression: \"", // 9 columns
line,
"\"\n",
" ",
std::string(column - 1, ' '),
std::string(spacing - 1, ' '),
"^\n");
}
char ParserBase::next()
{
char ch = *m_it;
// See https://www.gnu.org/prep/standards/standards.html#Errors
if (ch == '\0')
{
return '\0';
}
else
advance_rowcol(ch, row, column);
return *++m_it;
}
void ParserBase::add_error(std::string message, const ParserBase::SourceLoc& loc)
{
// avoid cascading errors by only saving the first
@ -47,15 +79,19 @@ namespace vcpkg::Parse
auto lineend = loc.it;
while (*lineend != '\n' && *lineend != '\r' && *lineend != '\0')
++lineend;
m_err.reset(
new ParseError(m_origin.c_str(), loc.row, loc.column, {linestart, lineend}, std::move(message)));
m_err.reset(new ParseError(m_origin.c_str(),
loc.row,
loc.column,
static_cast<int>(loc.it - linestart),
{linestart, lineend},
std::move(message)));
}
// Avoid error loops by skipping to the end
skip_to_eof();
}
static Optional<std::string> remove_field(RawParagraph* fields, const std::string& fieldname)
static Optional<std::pair<std::string, TextRowCol>> remove_field(Paragraph* fields, const std::string& fieldname)
{
auto it = fields->find(fieldname);
if (it == fields->end())
@ -63,12 +99,12 @@ namespace vcpkg::Parse
return nullopt;
}
const std::string value = std::move(it->second);
auto value = std::move(it->second);
fields->erase(it);
return value;
}
void ParagraphParser::required_field(const std::string& fieldname, std::string& out)
void ParagraphParser::required_field(const std::string& fieldname, std::pair<std::string&, TextRowCol&> out)
{
auto maybe_field = remove_field(&fields, fieldname);
if (const auto field = maybe_field.get())
@ -76,10 +112,24 @@ namespace vcpkg::Parse
else
missing_fields.push_back(fieldname);
}
std::string ParagraphParser::optional_field(const std::string& fieldname) const
void ParagraphParser::optional_field(const std::string& fieldname, std::pair<std::string&, TextRowCol&> out)
{
return remove_field(&fields, fieldname).value_or("");
auto maybe_field = remove_field(&fields, fieldname);
if (auto field = maybe_field.get()) out = std::move(*field);
}
void ParagraphParser::required_field(const std::string& fieldname, std::string& out)
{
TextRowCol ignore;
required_field(fieldname, {out, ignore});
}
std::string ParagraphParser::optional_field(const std::string& fieldname)
{
std::string out;
TextRowCol ignore;
optional_field(fieldname, {out, ignore});
return out;
}
std::unique_ptr<ParseControlErrorInfo> ParagraphParser::error_info(const std::string& name) const
{
if (!fields.empty() || !missing_fields.empty())
@ -116,29 +166,34 @@ namespace vcpkg::Parse
} while (true);
}
ExpectedS<std::vector<std::string>> parse_default_features_list(const std::string& str, CStringView origin)
ExpectedS<std::vector<std::string>> parse_default_features_list(const std::string& str,
CStringView origin,
TextRowCol textrowcol)
{
Parse::ParserBase parser;
parser.init(str, origin);
parser.init(str, origin, textrowcol);
auto opt = parse_list_until_eof<std::string>("default features", parser, &parse_feature_name);
if (!opt) return {parser.get_error()->format(), expected_right_tag};
return {std::move(opt).value_or_exit(VCPKG_LINE_INFO), expected_left_tag};
}
ExpectedS<std::vector<ParsedQualifiedSpecifier>> parse_qualified_specifier_list(const std::string& str,
CStringView origin)
CStringView origin,
TextRowCol textrowcol)
{
Parse::ParserBase parser;
parser.init(str, origin);
parser.init(str, origin, textrowcol);
auto opt = parse_list_until_eof<ParsedQualifiedSpecifier>(
"dependencies", parser, [](ParserBase& parser) { return parse_qualified_specifier(parser); });
if (!opt) return {parser.get_error()->format(), expected_right_tag};
return {std::move(opt).value_or_exit(VCPKG_LINE_INFO), expected_left_tag};
}
ExpectedS<std::vector<Dependency>> parse_dependencies_list(const std::string& str, CStringView origin)
ExpectedS<std::vector<Dependency>> parse_dependencies_list(const std::string& str,
CStringView origin,
TextRowCol textrowcol)
{
Parse::ParserBase parser;
parser.init(str, origin);
parser.init(str, origin, textrowcol);
auto opt = parse_list_until_eof<Dependency>("dependencies", parser, [](ParserBase& parser) {
auto loc = parser.cur_loc();
return parse_qualified_specifier(parser).then([&](ParsedQualifiedSpecifier&& pqs) -> Optional<Dependency> {

View File

@ -115,8 +115,10 @@ namespace vcpkg
return Type{Type::UNKNOWN};
}
static ParseExpected<SourceParagraph> parse_source_paragraph(const fs::path& path_to_control, RawParagraph&& fields)
static ParseExpected<SourceParagraph> parse_source_paragraph(const fs::path& path_to_control, Paragraph&& fields)
{
auto origin = path_to_control.u8string();
ParagraphParser parser(std::move(fields));
auto spgh = std::make_unique<SourceParagraph>();
@ -127,23 +129,25 @@ namespace vcpkg
spgh->description = parser.optional_field(SourceParagraphFields::DESCRIPTION);
spgh->maintainer = parser.optional_field(SourceParagraphFields::MAINTAINER);
spgh->homepage = parser.optional_field(SourceParagraphFields::HOMEPAGE);
spgh->depends = parse_dependencies_list(parser.optional_field(SourceParagraphFields::BUILD_DEPENDS))
.value_or_exit(VCPKG_LINE_INFO);
spgh->default_features =
parse_default_features_list(parser.optional_field(SourceParagraphFields::DEFAULTFEATURES))
.value_or_exit(VCPKG_LINE_INFO);
TextRowCol textrowcol;
std::string buf;
parser.optional_field(SourceParagraphFields::BUILD_DEPENDS, {buf, textrowcol});
spgh->depends = parse_dependencies_list(buf, origin, textrowcol).value_or_exit(VCPKG_LINE_INFO);
buf.clear();
parser.optional_field(SourceParagraphFields::DEFAULTFEATURES, {buf, textrowcol});
spgh->default_features = parse_default_features_list(buf, origin, textrowcol).value_or_exit(VCPKG_LINE_INFO);
spgh->supports_expression = parser.optional_field(SourceParagraphFields::SUPPORTS);
spgh->type = Type::from_string(parser.optional_field(SourceParagraphFields::TYPE));
auto err = parser.error_info(spgh->name.empty() ? path_to_control.u8string() : spgh->name);
auto err = parser.error_info(spgh->name.empty() ? origin : spgh->name);
if (err)
return err;
else
return spgh;
}
static ParseExpected<FeatureParagraph> parse_feature_paragraph(const fs::path& path_to_control,
RawParagraph&& fields)
static ParseExpected<FeatureParagraph> parse_feature_paragraph(const fs::path& path_to_control, Paragraph&& fields)
{
auto origin = path_to_control.u8string();
ParagraphParser parser(std::move(fields));
auto fpgh = std::make_unique<FeatureParagraph>();
@ -151,10 +155,10 @@ namespace vcpkg
parser.required_field(SourceParagraphFields::FEATURE, fpgh->name);
parser.required_field(SourceParagraphFields::DESCRIPTION, fpgh->description);
fpgh->depends = parse_dependencies_list(parser.optional_field(SourceParagraphFields::BUILD_DEPENDS))
fpgh->depends = parse_dependencies_list(parser.optional_field(SourceParagraphFields::BUILD_DEPENDS), origin)
.value_or_exit(VCPKG_LINE_INFO);
auto err = parser.error_info(fpgh->name.empty() ? path_to_control.u8string() : fpgh->name);
auto err = parser.error_info(fpgh->name.empty() ? origin : fpgh->name);
if (err)
return err;
else
@ -162,7 +166,7 @@ namespace vcpkg
}
ParseExpected<SourceControlFile> SourceControlFile::parse_control_file(
const fs::path& path_to_control, std::vector<Parse::RawParagraph>&& control_paragraphs)
const fs::path& path_to_control, std::vector<Parse::Paragraph>&& control_paragraphs)
{
if (control_paragraphs.size() == 0)
{

View File

@ -24,12 +24,12 @@ namespace vcpkg
.push_back('\n');
}
StatusParagraph::StatusParagraph(Parse::RawParagraph&& fields)
StatusParagraph::StatusParagraph(Parse::Paragraph&& fields)
: want(Want::ERROR_STATE), state(InstallState::ERROR_STATE)
{
auto status_it = fields.find(BinaryParagraphRequiredField::STATUS);
Checks::check_exit(VCPKG_LINE_INFO, status_it != fields.end(), "Expected 'Status' field in status paragraph");
std::string status_field = std::move(status_it->second);
std::string status_field = std::move(status_it->second.first);
fields.erase(status_it);
this->package = BinaryParagraph(std::move(fields));

View File

@ -51,7 +51,7 @@ namespace vcpkg
{
const auto& pghs = *p_pghs;
Parse::RawParagraph keys;
Parse::Paragraph keys;
if (pghs.size() > 0) keys = pghs[0];
for (size_t x = 1; x < pghs.size(); ++x)
@ -60,10 +60,10 @@ namespace vcpkg
keys.insert(p);
}
ret.user_id = keys["User-Id"];
ret.user_time = keys["User-Since"];
ret.user_mac = keys["Mac-Hash"];
ret.last_completed_survey = keys["Survey-Completed"];
ret.user_id = keys["User-Id"].first;
ret.user_time = keys["User-Since"].first;
ret.user_mac = keys["Mac-Hash"].first;
ret.last_completed_survey = keys["Survey-Completed"].first;
}
}
catch (...)