aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2021-10-05 18:21:10 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2021-10-07 13:19:36 +0300
commit27c616e238b6891cd0fe4081d614e6b5c1f977bb (patch)
tree8ed3bd9320918b6a25db7f6c5602e7ba0311e4af
parent9d54661016b1e3d6ea3a6bc633bd657d25e99122 (diff)
Add repositories manifest header
-rw-r--r--libbpkg/manifest.cxx172
-rw-r--r--libbpkg/manifest.hxx17
-rw-r--r--tests/manifest/driver.cxx10
-rw-r--r--tests/manifest/testscript143
4 files changed, 325 insertions, 17 deletions
diff --git a/libbpkg/manifest.cxx b/libbpkg/manifest.cxx
index f1b50a3..5638d1b 100644
--- a/libbpkg/manifest.cxx
+++ b/libbpkg/manifest.cxx
@@ -4080,7 +4080,8 @@ namespace bpkg
parse_repository_manifest (parser& p,
name_value nv,
repository_type base_type,
- bool iu)
+ bool iu,
+ bool verify_version = true)
{
auto bad_name ([&p, &nv](const string& d) {
throw parsing (p.name (), nv.name_line, nv.name_column, d);});
@@ -4090,11 +4091,16 @@ namespace bpkg
// Make sure this is the start and we support the version.
//
- if (!nv.name.empty ())
- bad_name ("start of repository manifest expected");
+ if (verify_version)
+ {
+ if (!nv.name.empty ())
+ bad_name ("start of repository manifest expected");
- if (nv.value != "1")
- bad_value ("unsupported format version");
+ if (nv.value != "1")
+ bad_value ("unsupported format version");
+
+ nv = p.next ();
+ }
repository_manifest r;
@@ -4105,7 +4111,7 @@ namespace bpkg
optional<repository_type> type;
optional<name_value> location;
- for (nv = p.next (); !nv.empty (); nv = p.next ())
+ for (; !nv.empty (); nv = p.next ())
{
string& n (nv.name);
string& v (nv.value);
@@ -4450,13 +4456,126 @@ namespace bpkg
parse_repository_manifests (parser& p,
repository_type base_type,
bool iu,
+ optional<repositories_manifest_header>& header,
vector<repository_manifest>& ms)
{
+ // Return nullopt on eos. Otherwise, parse and verify the
+ // manifest-starting format version value and return the subsequent
+ // manifest value, that can potentially be empty (for an empty manifest).
+ //
+ // Also save the manifest-starting position (start_nv) for the
+ // diagnostics.
+ //
+ name_value start_nv;
+ auto next_manifest = [&p, &start_nv] () -> optional<name_value>
+ {
+ start_nv = p.next ();
+
+ if (start_nv.empty ())
+ return nullopt;
+
+ // Make sure this is the start and we support the version.
+ //
+ if (!start_nv.name.empty ())
+ throw parsing (p.name (), start_nv.name_line, start_nv.name_column,
+ "start of repository manifest expected");
+
+ if (start_nv.value != "1")
+ throw parsing (p.name (), start_nv.value_line, start_nv.value_column,
+ "unsupported format version");
+
+ return p.next ();
+ };
+
+ optional<name_value> nv (next_manifest ());
+
+ if (!nv)
+ throw parsing (p.name (), start_nv.name_line, start_nv.name_column,
+ "start of repository manifest expected");
+
+ auto bad_name ([&p, &nv](const string& d) {
+ throw parsing (p.name (), nv->name_line, nv->name_column, d);});
+
+ auto bad_value ([&p, &nv](const string& d) {
+ throw parsing (p.name (), nv->value_line, nv->value_column, d);});
+
+ // First check if this a header manifest, if any manifest is present.
+ //
+ // Note that if this is none of the known header values, then we assume
+ // this is a repository manifest (rather than a header that starts with an
+ // unknown value; so use one of the original names to make sure it's
+ // recognized as such, for example `compression:none`).
+ //
+ if (nv->name == "min-bpkg-version" ||
+ nv->name == "compression")
+ {
+ header = repositories_manifest_header ();
+
+ // First verify the version, if any.
+ //
+ if (nv->name == "min-bpkg-version")
+ try
+ {
+ const string& v (nv->value);
+ standard_version mbv (v, standard_version::allow_earliest);
+
+ if (mbv > standard_version (LIBBPKG_VERSION_STR))
+ bad_value (
+ "incompatible repositories manifest: minimum bpkg version is " + v);
+
+ header->min_bpkg_version = move (mbv);
+
+ nv = p.next ();
+ }
+ catch (const invalid_argument& e)
+ {
+ bad_value (string ("invalid minimum bpkg version: ") + e.what ());
+ }
+
+ // Parse the remaining header values, failing if min-bpkg-version is
+ // encountered (should be first).
+ //
+ for (; !nv->empty (); nv = p.next ())
+ {
+ const string& n (nv->name);
+ string& v (nv->value);
+
+ if (n == "min-bpkg-version")
+ {
+ bad_name ("minimum bpkg version must be first in repositories "
+ "manifest header");
+ }
+ else if (n == "compression")
+ {
+ header->compression = move (v);
+ }
+ else if (!iu)
+ bad_name ("unknown name '" + n + "' in repositories manifest header");
+ }
+
+ nv = next_manifest ();
+ }
+
+ // Parse the manifest list.
+ //
+ // Note that if nv is present, then it contains the manifest's first
+ // value, which can potentially be empty (for an empty manifest, which is
+ // recognized as a base manifest).
+ //
+ // Also note that if the header is present but is not followed by
+ // repository manifests (there is no ':' line after the header values),
+ // then the empty manifest list is returned (no base manifest is
+ // automatically added).
+ //
bool base (false);
- for (name_value nv (p.next ()); !nv.empty (); nv = p.next ())
+ while (nv)
{
- ms.push_back (parse_repository_manifest (p, nv, base_type, iu));
+ ms.push_back (parse_repository_manifest (p,
+ *nv,
+ base_type,
+ iu,
+ false /* verify_version */));
// Make sure that there is a single base repository manifest in the
// list.
@@ -4464,19 +4583,38 @@ namespace bpkg
if (ms.back ().effective_role () == repository_role::base)
{
if (base)
- throw parsing (p.name (), nv.name_line, nv.name_column,
+ throw parsing (p.name (), start_nv.name_line, start_nv.name_column,
"base repository manifest redefinition");
base = true;
}
+
+ nv = next_manifest ();
}
}
// Serialize the repository manifest list.
//
static void
- serialize_repository_manifests (serializer& s,
- const vector<repository_manifest>& ms)
+ serialize_repository_manifests (
+ serializer& s,
+ const optional<repositories_manifest_header>& header,
+ const vector<repository_manifest>& ms)
{
+ if (header)
+ {
+ s.next ("", "1"); // Start of manifest.
+
+ const repositories_manifest_header& h (*header);
+
+ if (h.min_bpkg_version)
+ s.next ("min-bpkg-version", h.min_bpkg_version->string ());
+
+ if (h.compression)
+ s.next ("compression", *h.compression);
+
+ s.next ("", ""); // End of manifest.
+ }
+
for (const repository_manifest& r: ms)
r.serialize (s);
@@ -4488,13 +4626,13 @@ namespace bpkg
pkg_repository_manifests::
pkg_repository_manifests (parser& p, bool iu)
{
- parse_repository_manifests (p, repository_type::pkg, iu, *this);
+ parse_repository_manifests (p, repository_type::pkg, iu, header, *this);
}
void pkg_repository_manifests::
serialize (serializer& s) const
{
- serialize_repository_manifests (s, *this);
+ serialize_repository_manifests (s, header, *this);
}
// dir_repository_manifests
@@ -4502,13 +4640,13 @@ namespace bpkg
dir_repository_manifests::
dir_repository_manifests (parser& p, bool iu)
{
- parse_repository_manifests (p, repository_type::dir, iu, *this);
+ parse_repository_manifests (p, repository_type::dir, iu, header, *this);
}
void dir_repository_manifests::
serialize (serializer& s) const
{
- serialize_repository_manifests (s, *this);
+ serialize_repository_manifests (s, header, *this);
}
// git_repository_manifests
@@ -4516,13 +4654,13 @@ namespace bpkg
git_repository_manifests::
git_repository_manifests (parser& p, bool iu)
{
- parse_repository_manifests (p, repository_type::git, iu, *this);
+ parse_repository_manifests (p, repository_type::git, iu, header, *this);
}
void git_repository_manifests::
serialize (serializer& s) const
{
- serialize_repository_manifests (s, *this);
+ serialize_repository_manifests (s, header, *this);
}
// signature_manifest
diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx
index ca54cdc..77043b0 100644
--- a/libbpkg/manifest.hxx
+++ b/libbpkg/manifest.hxx
@@ -18,6 +18,7 @@
#include <libbutl/path.hxx>
#include <libbutl/optional.hxx>
#include <libbutl/small-vector.hxx>
+#include <libbutl/standard-version.hxx>
#include <libbutl/manifest-forward.hxx>
#include <libbpkg/package-name.hxx>
@@ -1524,6 +1525,13 @@ namespace bpkg
butl::manifest_name_value start,
bool ignore_unknown = false);
+ struct repositories_manifest_header
+ {
+ public:
+ butl::optional<butl::standard_version> min_bpkg_version;
+ butl::optional<std::string> compression;
+ };
+
class LIBBPKG_EXPORT pkg_repository_manifests:
public std::vector<repository_manifest>
{
@@ -1532,6 +1540,9 @@ namespace bpkg
using base_type::base_type;
+ butl::optional<repositories_manifest_header> header;
+
+ public:
pkg_repository_manifests () = default;
pkg_repository_manifests (butl::manifest_parser&,
bool ignore_unknown = false);
@@ -1548,6 +1559,9 @@ namespace bpkg
using base_type::base_type;
+ butl::optional<repositories_manifest_header> header;
+
+ public:
dir_repository_manifests () = default;
dir_repository_manifests (butl::manifest_parser&,
bool ignore_unknown = false);
@@ -1564,6 +1578,9 @@ namespace bpkg
using base_type::base_type;
+ butl::optional<repositories_manifest_header> header;
+
+ public:
git_repository_manifests () = default;
git_repository_manifests (butl::manifest_parser&,
bool ignore_unknown = false);
diff --git a/tests/manifest/driver.cxx b/tests/manifest/driver.cxx
index 44b1a79..273dce5 100644
--- a/tests/manifest/driver.cxx
+++ b/tests/manifest/driver.cxx
@@ -23,6 +23,7 @@ using namespace bpkg;
// argv[0] (-pp|-dp|-gp|-pr|-dr|-gr|-s)
// argv[0] -p -c -i
// argv[0] -ec <version>
+// argv[0] -v
//
// In the first form read and parse manifest list from stdin and serialize it
// to stdout. The following options specify the manifest type.
@@ -34,6 +35,7 @@ using namespace bpkg;
// -dr parse dir repository manifest list
// -gr parse git repository manifest list
// -s parse signature manifest
+// -v print the libbpkg version
//
// In the second form read and parse the package manifest from stdin and
// serialize it to stdout.
@@ -47,6 +49,8 @@ using namespace bpkg;
// roundtrip them to stdout together with their effective constraints,
// calculated using version passed as an argument.
//
+// In the forth form print the libbpkg version to stdout and exit.
+//
int
main (int argc, char* argv[])
{
@@ -55,6 +59,12 @@ main (int argc, char* argv[])
cout.exceptions (ios_base::failbit | ios_base::badbit);
+ if (mode == "-v")
+ {
+ cout << standard_version (LIBBPKG_VERSION_STR) << endl;
+ return 0;
+ }
+
manifest_parser p (cin, "stdin");
manifest_serializer s (cout, "stdout");
diff --git a/tests/manifest/testscript b/tests/manifest/testscript
index 25e0ae3..c35f618 100644
--- a/tests/manifest/testscript
+++ b/tests/manifest/testscript
@@ -935,6 +935,149 @@
: repository-list
:
{
+ : header
+ :
+ {
+ +$* -v | set v
+
+ test.options += -pr
+
+ : version
+ :
+ {
+ $* <<"EOF" >>"EOF"
+ : 1
+ min-bpkg-version: $v
+ :
+ location: http://pkg.example.org/1/math
+ type: pkg
+ role: prerequisite
+ :
+ url: http://cppget.org
+ email: repoman@cppget.org; General mailing list.
+ EOF
+ }
+
+ : invalid-version
+ :
+ {
+ $* <<EOI 2>'stdin:2:19: error: invalid minimum bpkg version: invalid major version' != 0
+ : 1
+ min-bpkg-version: foo
+ EOI
+ }
+
+ : too-new
+ :
+ {
+ $* <<EOI 2>'stdin:2:19: error: incompatible repositories manifest: minimum bpkg version is 1000.0.0' != 0
+ : 1
+ min-bpkg-version: 1000.0.0
+ EOI
+ }
+
+ : non-version
+ :
+ {
+ $* <<EOF >>EOF
+ : 1
+ compression: none
+ :
+ location: http://pkg.example.org/1/math
+ type: pkg
+ role: prerequisite
+ :
+ url: http://cppget.org
+ email: repoman@cppget.org; General mailing list.
+ EOF
+ }
+
+ : version-non-first
+ :
+ {
+ $* <<"EOI" 2>"stdin:3:1: error: minimum bpkg version must be first in repositories manifest header" != 0
+ : 1
+ compression: none
+ min-bpkg-version: $v
+ EOI
+ }
+
+ : unknown-header-value
+ :
+ {
+ $* <<EOI 2>"stdin:3:1: error: unknown name 'unknown' in repositories manifest header" != 0
+ : 1
+ compression: none
+ unknown: foo
+ EOI
+ }
+
+ : unknown-manifest-value
+ :
+ {
+ $* <<EOI 2>"stdin:2:1: error: unknown name 'unknown' in repository manifest" != 0
+ : 1
+ unknown: foo
+ EOI
+ }
+
+ : empty-repository-manifest
+ :
+ {
+ $* <<EOF >>EOF
+ : 1
+ compression: none
+ :
+ EOF
+ }
+
+ : base-redefinition
+ :
+ {
+ $* <<EOI 2>'stdin:4:1: error: base repository manifest redefinition' != 0
+ : 1
+ compression: none
+ :
+ :
+ EOI
+ }
+
+ : empty-base
+ :
+ {
+ $* <<"EOF" >>"EOF"
+ : 1
+ compression: none
+ :
+ location: http://pkg.example.org/1/math
+ type: pkg
+ role: prerequisite
+ :
+ EOF
+ }
+
+ : no-repository-manifest
+ :
+ {
+ $* <<EOF >>EOF
+ : 1
+ compression: none
+ EOF
+ }
+
+ : only-empty-base
+ :
+ {
+ $* <<EOF >>EOF
+ : 1
+ EOF
+ }
+
+ : empty-manifest-list
+ :
+ $* <'' 2>'stdin:2:1: error: start of repository manifest expected' != 0
+ }
+
: pkg
:
{