aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2018-02-14 19:27:47 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2018-02-15 11:08:22 +0300
commit18e40e91c99088ef5fb9458ec4c1ec21ddcc51e6 (patch)
treebbb91a98a375e9cae387f03a7ca1ef32db202d73
parent02458785b93094dfc65b31a4ecf2a8e690143b03 (diff)
Add support for git manifests
-rw-r--r--libbpkg/manifest.cxx896
-rw-r--r--libbpkg/manifest.hxx165
-rw-r--r--tests/manifest/driver.cxx18
-rw-r--r--tests/manifest/testscript358
-rw-r--r--tests/repository-location/driver.cxx44
5 files changed, 1011 insertions, 470 deletions
diff --git a/libbpkg/manifest.cxx b/libbpkg/manifest.cxx
index 1c0650e..d58b6d8 100644
--- a/libbpkg/manifest.cxx
+++ b/libbpkg/manifest.cxx
@@ -604,26 +604,119 @@ namespace bpkg
// package_manifest
//
- package_manifest::
- package_manifest (parser& p, bool iu)
- : package_manifest (p, p.next (), false, iu) // Delegate
+ void package_manifest::
+ serialize (serializer& s) const
{
- // Make sure this is the end.
+ // @@ Should we check that all non-optional values are specified ?
+ // @@ Should we check that values are valid: name is not empty, version
+ // release is not empty, sha256sum is a proper string, ...?
+ // @@ Currently we don't know if we are serializing the individual package
+ // manifest or the package list manifest, so can't ensure all values
+ // allowed in the current context (*-file values).
//
- name_value nv (p.next ());
- if (!nv.empty ())
- throw parsing (p.name (), nv.name_line, nv.name_column,
- "single package manifest expected");
- }
- package_manifest::
- package_manifest (parser& p, name_value nv, bool iu)
- : package_manifest (p, nv, true, iu) // Delegate
- {
+ s.next ("", "1"); // Start of manifest.
+ s.next ("name", name);
+ s.next ("version", version.string ());
+
+ if (priority)
+ {
+ size_t v (*priority);
+ assert (v < priority_names.size ());
+
+ s.next ("priority",
+ serializer::merge_comment (priority_names[v],
+ priority->comment));
+ }
+
+ s.next ("summary", summary);
+
+ for (const auto& la: license_alternatives)
+ s.next ("license",
+ serializer::merge_comment (concatenate (la), la.comment));
+
+ if (!tags.empty ())
+ s.next ("tags", concatenate (tags));
+
+ if (description)
+ {
+ if (description->file)
+ s.next ("description-file",
+ serializer::merge_comment (description->path.string (),
+ description->comment));
+ else
+ s.next ("description", description->text);
+ }
+
+ for (const auto& c: changes)
+ {
+ if (c.file)
+ s.next ("changes-file",
+ serializer::merge_comment (c.path.string (), c.comment));
+ else
+ s.next ("changes", c.text);
+ }
+
+ s.next ("url", serializer::merge_comment (url, url.comment));
+ if (doc_url)
+ s.next ("doc-url",
+ serializer::merge_comment (*doc_url, doc_url->comment));
+
+ if (src_url)
+ s.next ("src-url",
+ serializer::merge_comment (*src_url, src_url->comment));
+
+ if (package_url)
+ s.next ("package-url",
+ serializer::merge_comment (*package_url,
+ package_url->comment));
+
+ s.next ("email", serializer::merge_comment (email, email.comment));
+
+ if (package_email)
+ s.next ("package-email",
+ serializer::merge_comment (*package_email,
+ package_email->comment));
+
+ if (build_email)
+ s.next ("build-email",
+ serializer::merge_comment (*build_email,
+ build_email->comment));
+
+ for (const auto& d: dependencies)
+ s.next ("depends",
+ (d.conditional
+ ? (d.buildtime ? "?* " : "? ")
+ : (d.buildtime ? "* " : "")) +
+ serializer::merge_comment (concatenate (d, " | "), d.comment));
+
+ for (const auto& r: requirements)
+ s.next ("requires",
+ (r.conditional
+ ? (r.buildtime ? "?* " : "? ")
+ : (r.buildtime ? "* " : "")) +
+ serializer::merge_comment (concatenate (r, " | "), r.comment));
+
+ for (const auto& c: build_constraints)
+ s.next (c.exclusion ? "build-exclude" : "build-include",
+ serializer::merge_comment (!c.target
+ ? c.config
+ : c.config + "/" + *c.target,
+ c.comment));
+
+ if (location)
+ s.next ("location", location->posix_string ());
+
+ if (sha256sum)
+ s.next ("sha256sum", *sha256sum);
+
+ s.next ("", ""); // End of manifest.
}
- package_manifest::
- package_manifest (parser& p, name_value nv, bool il, bool iu)
+ // bpkg_package_manifest
+ //
+ static package_manifest
+ bpkg_package_manifest (parser& p, name_value nv, bool il, bool iu)
{
auto bad_name ([&p, &nv](const string& d) {
throw parsing (p.name (), nv.name_line, nv.name_column, d);});
@@ -639,7 +732,9 @@ namespace bpkg
if (nv.value != "1")
bad_value ("unsupported format version");
- auto add_build_constraint = [&bad_value, this] (bool e, const string& vc)
+ package_manifest r;
+
+ auto add_build_constraint = [&bad_value, &r] (bool e, const string& vc)
{
auto vcp (parser::split_comment (vc));
string v (move (vcp.first));
@@ -657,30 +752,29 @@ namespace bpkg
if (tg && tg->empty ())
bad_value ("empty build target pattern");
- build_constraints.emplace_back (e, move (nm), move (tg), move (c));
+ r.build_constraints.emplace_back (e, move (nm), move (tg), move (c));
};
- auto parse_url = [&bad_value] (const string& v,
- const char* what) -> url_type
+ auto parse_url = [&bad_value] (const string& v, const char* what) -> url
{
auto p (parser::split_comment (v));
if (v.empty ())
bad_value (string ("empty ") + what + " url");
- return url_type (move (p.first), move (p.second));
+ return url (move (p.first), move (p.second));
};
auto parse_email = [&bad_value] (const string& v,
const char* what,
- bool empty = false) -> email_type
+ bool empty = false) -> email
{
auto p (parser::split_comment (v));
if (v.empty () && !empty)
bad_value (string ("empty ") + what + " email");
- return email_type (move (p.first), move (p.second));
+ return email (move (p.first), move (p.second));
};
for (nv = p.next (); !nv.empty (); nv = p.next ())
@@ -690,22 +784,22 @@ namespace bpkg
if (n == "name")
{
- if (!name.empty ())
+ if (!r.name.empty ())
bad_name ("package name redefinition");
if (v.empty ())
bad_value ("empty package name");
- name = move (v);
+ r.name = move (v);
}
else if (n == "version")
{
- if (!version.empty ())
+ if (!r.version.empty ())
bad_name ("package version redefinition");
try
{
- version = version_type (move (v));
+ r.version = version (move (v));
}
catch (const invalid_argument& e)
{
@@ -715,22 +809,22 @@ namespace bpkg
// Versions like 1.2.3- are forbidden in manifest as intended to be
// used for version constrains rather than actual releases.
//
- if (version.release && version.release->empty ())
+ if (r.version.release && r.version.release->empty ())
bad_value ("invalid package version release");
}
else if (n == "summary")
{
- if (!summary.empty ())
+ if (!r.summary.empty ())
bad_name ("package summary redefinition");
if (v.empty ())
bad_value ("empty package summary");
- summary = move (v);
+ r.summary = move (v);
}
else if (n == "tags")
{
- if (!tags.empty ())
+ if (!r.tags.empty ())
bad_name ("package tags redefinition");
list_parser lp (v.begin (), v.end ());
@@ -739,17 +833,17 @@ namespace bpkg
if (lv.find_first_of (spaces) != string::npos)
bad_value ("only single-word tags allowed");
- tags.push_back (move (lv));
+ r.tags.push_back (move (lv));
}
- if (tags.empty ())
+ if (r.tags.empty ())
bad_value ("empty package tags specification");
}
else if (n == "description")
{
- if (description)
+ if (r.description)
{
- if (description->file)
+ if (r.description->file)
bad_name ("package description and description-file are "
"mutually exclusive");
else
@@ -759,16 +853,16 @@ namespace bpkg
if (v.empty ())
bad_value ("empty package description");
- description = text_file (move (v));
+ r.description = text_file (move (v));
}
else if (n == "description-file")
{
if (il)
bad_name ("package description-file not allowed");
- if (description)
+ if (r.description)
{
- if (description->file)
+ if (r.description->file)
bad_name ("package description-file redefinition");
else
bad_name ("package description-file and description are "
@@ -784,14 +878,14 @@ namespace bpkg
if (p.absolute ())
bad_value ("package description-file path is absolute");
- description = text_file (move (p), move (vc.second));
+ r.description = text_file (move (p), move (vc.second));
}
else if (n == "changes")
{
if (v.empty ())
bad_value ("empty package changes specification");
- changes.emplace_back (move (v));
+ r.changes.emplace_back (move (v));
}
else if (n == "changes-file")
{
@@ -807,60 +901,60 @@ namespace bpkg
if (p.absolute ())
bad_value ("package changes-file path is absolute");
- changes.emplace_back (move (p), move (vc.second));
+ r.changes.emplace_back (move (p), move (vc.second));
}
else if (n == "url")
{
- if (!url.empty ())
+ if (!r.url.empty ())
bad_name ("project url redefinition");
- url = parse_url (v, "project");
+ r.url = parse_url (v, "project");
}
else if (n == "email")
{
- if (!email.empty ())
+ if (!r.email.empty ())
bad_name ("project email redefinition");
- email = parse_email (v, "project");
+ r.email = parse_email (v, "project");
}
else if (n == "doc-url")
{
- if (doc_url)
+ if (r.doc_url)
bad_name ("doc url redefinition");
- doc_url = parse_url (v, "doc");
+ r.doc_url = parse_url (v, "doc");
}
else if (n == "src-url")
{
- if (src_url)
+ if (r.src_url)
bad_name ("src url redefinition");
- src_url = parse_url (v, "src");
+ r.src_url = parse_url (v, "src");
}
else if (n == "package-url")
{
- if (package_url)
+ if (r.package_url)
bad_name ("package url redefinition");
- package_url = parse_url (v, "package");
+ r.package_url = parse_url (v, "package");
}
else if (n == "package-email")
{
- if (package_email)
+ if (r.package_email)
bad_name ("package email redefinition");
- package_email = parse_email (v, "package");
+ r.package_email = parse_email (v, "package");
}
else if (n == "build-email")
{
- if (build_email)
+ if (r.build_email)
bad_name ("build email redefinition");
- build_email = parse_email (v, "build", true);
+ r.build_email = parse_email (v, "build", true);
}
else if (n == "priority")
{
- if (priority)
+ if (r.priority)
bad_name ("package priority redefinition");
auto vc (parser::split_comment (v));
@@ -871,9 +965,9 @@ namespace bpkg
if (i == e)
bad_value ("invalid package priority");
- priority =
- priority_type (static_cast<priority_type::value_type> (i - b),
- move (vc.second));
+ r.priority =
+ priority (static_cast<priority::value_type> (i - b),
+ move (vc.second));
}
else if (n == "license")
{
@@ -887,7 +981,7 @@ namespace bpkg
if (l.empty ())
bad_value ("empty package license specification");
- license_alternatives.push_back (move (l));
+ r.license_alternatives.push_back (move (l));
}
else if (n == "requires")
{
@@ -918,7 +1012,7 @@ namespace bpkg
if (ra.empty () && ra.comment.empty ())
bad_value ("empty package requirement specification");
- requirements.push_back (move (ra));
+ r.requirements.push_back (move (ra));
}
else if (n == "build-include")
{
@@ -1000,11 +1094,11 @@ namespace bpkg
if (pos == string::npos)
bad_value (no_max_version);
- version_type min_version;
+ version min_version;
try
{
- min_version = version_type (string (i, b + pos));
+ min_version = version (string (i, b + pos));
}
catch (const invalid_argument& e)
{
@@ -1027,11 +1121,11 @@ namespace bpkg
if (pos == string::npos)
bad_value (invalid_range);
- version_type max_version;
+ version max_version;
try
{
- max_version = version_type (string (i, b + pos));
+ max_version = version (string (i, b + pos));
}
catch (const invalid_argument& e)
{
@@ -1101,11 +1195,11 @@ namespace bpkg
if (pos == string::npos)
bad_value ("no prerequisite package version specified");
- version_type v;
+ version v;
try
{
- v = version_type (lv.c_str () + pos);
+ v = version (lv.c_str () + pos);
}
catch (const invalid_argument& e)
{
@@ -1141,14 +1235,14 @@ namespace bpkg
if (da.empty ())
bad_value ("empty package dependency specification");
- dependencies.push_back (da);
+ r.dependencies.push_back (da);
}
else if (n == "location")
{
if (!il)
bad_name ("package location not allowed");
- if (location)
+ if (r.location)
bad_name ("package location redefinition");
try
@@ -1161,7 +1255,7 @@ namespace bpkg
if (l.absolute ())
bad_value ("absolute package location");
- location = move (l);
+ r.location = move (l);
}
catch (const invalid_path&)
{
@@ -1173,13 +1267,13 @@ namespace bpkg
if (!il)
bad_name ("package sha256sum not allowed");
- if (sha256sum)
+ if (r.sha256sum)
bad_name ("package sha256sum redefinition");
if (!valid_sha256 (v))
bad_value ("invalid package sha256sum");
- sha256sum = move (v);
+ r.sha256sum = move (v);
}
else if (!iu)
bad_name ("unknown name '" + n + "' in package manifest");
@@ -1187,140 +1281,151 @@ namespace bpkg
// Verify all non-optional values were specified.
//
- if (name.empty ())
+ if (r.name.empty ())
bad_value ("no package name specified");
- else if (version.empty ())
+ else if (r.version.empty ())
bad_value ("no package version specified");
- else if (summary.empty ())
+ else if (r.summary.empty ())
bad_value ("no package summary specified");
- else if (url.empty ())
+ else if (r.url.empty ())
bad_value ("no project url specified");
- else if (email.empty ())
+ else if (r.email.empty ())
bad_value ("no project email specified");
- else if (license_alternatives.empty ())
+ else if (r.license_alternatives.empty ())
bad_value ("no project license specified");
if (il)
{
- if (!location)
+ if (!r.location)
bad_name ("no package location specified");
- if (!sha256sum)
+ if (!r.sha256sum)
bad_name ("no package sha256sum specified");
}
+
+ return r;
}
- void package_manifest::
- serialize (serializer& s) const
+ package_manifest
+ bpkg_package_manifest (parser& p, bool iu)
{
- // @@ Should we check that all non-optional values are specified ?
- // @@ Should we check that values are valid: name is not empty, version
- // release is not empty, sha256sum is a proper string, ...?
- // @@ Currently we don't know if we are serializing the individual package
- // manifest or the package list manifest, so can't ensure all values
- // allowed in the current context (location, sha256sum, *-file values).
+ package_manifest r (bpkg_package_manifest (p, p.next (), false, iu));
+
+ // Make sure this is the end.
//
+ name_value nv (p.next ());
+ if (!nv.empty ())
+ throw parsing (p.name (), nv.name_line, nv.name_column,
+ "single package manifest expected");
- s.next ("", "1"); // Start of manifest.
- s.next ("name", name);
- s.next ("version", version.string ());
+ return r;
+ }
- if (priority)
- {
- size_t v (*priority);
- assert (v < priority_names.size ());
+ package_manifest
+ bpkg_package_manifest (parser& p, name_value nv, bool iu)
+ {
+ return bpkg_package_manifest (p, nv, true, iu);
+ }
- s.next ("priority",
- serializer::merge_comment (priority_names[v],
- priority->comment));
- }
+ // git_package_manifest
+ //
+ package_manifest
+ git_package_manifest (parser& p, name_value nv, bool iu)
+ {
+ auto bad_name ([&p, &nv](const string& d) {
+ throw parsing (p.name (), nv.name_line, nv.name_column, d);});
- s.next ("summary", summary);
+ auto bad_value ([&p, &nv](const string& d) {
+ throw parsing (p.name (), nv.value_line, nv.value_column, d);});
- for (const auto& la: license_alternatives)
- s.next ("license",
- serializer::merge_comment (concatenate (la), la.comment));
+ // Make sure this is the start and we support the version.
+ //
+ if (!nv.name.empty ())
+ bad_name ("start of package manifest expected");
- if (!tags.empty ())
- s.next ("tags", concatenate (tags));
+ if (nv.value != "1")
+ bad_value ("unsupported format version");
- if (description)
- {
- if (description->file)
- s.next ("description-file",
- serializer::merge_comment (description->path.string (),
- description->comment));
- else
- s.next ("description", description->text);
- }
+ package_manifest r;
- for (const auto& c: changes)
+ for (nv = p.next (); !nv.empty (); nv = p.next ())
{
- if (c.file)
- s.next ("changes-file",
- serializer::merge_comment (c.path.string (), c.comment));
- else
- s.next ("changes", c.text);
- }
+ string& n (nv.name);
+ string& v (nv.value);
- s.next ("url", serializer::merge_comment (url, url.comment));
- if (doc_url)
- s.next ("doc-url",
- serializer::merge_comment (*doc_url, doc_url->comment));
+ if (n == "location")
+ {
+ if (r.location)
+ bad_name ("package location redefinition");
- if (src_url)
- s.next ("src-url",
- serializer::merge_comment (*src_url, src_url->comment));
+ try
+ {
+ path l (v);
- if (package_url)
- s.next ("package-url",
- serializer::merge_comment (*package_url, package_url->comment));
+ if (l.empty ())
+ bad_value ("empty package location");
- s.next ("email", serializer::merge_comment (email, email.comment));
+ if (l.absolute ())
+ bad_value ("absolute package location");
- if (package_email)
- s.next ("package-email",
- serializer::merge_comment (*package_email,
- package_email->comment));
+ // Make sure that the path is a directory (contains the trailing
+ // slash).
+ //
+ if (!l.to_directory ())
+ l = path_cast<dir_path> (move (l));
- if (build_email)
- s.next ("build-email",
- serializer::merge_comment (*build_email, build_email->comment));
+ r.location = move (l);
+ }
+ catch (const invalid_path&)
+ {
+ bad_value ("invalid package location");
+ }
+ }
+ else if (!iu)
+ bad_name ("unknown name '" + n + "' in package manifest");
+ }
- for (const auto& d: dependencies)
- s.next ("depends",
- (d.conditional
- ? (d.buildtime ? "?* " : "? ")
- : (d.buildtime ? "* " : "")) +
- serializer::merge_comment (concatenate (d, " | "), d.comment));
+ if (!r.location)
+ bad_name ("no package location specified");
- for (const auto& r: requirements)
- s.next ("requires",
- (r.conditional
- ? (r.buildtime ? "?* " : "? ")
- : (r.buildtime ? "* " : "")) +
- serializer::merge_comment (concatenate (r, " | "), r.comment));
+ return r;
+ }
- for (const auto& c: build_constraints)
- s.next (c.exclusion ? "build-exclude" : "build-include",
- serializer::merge_comment (!c.target
- ? c.config
- : c.config + "/" + *c.target,
- c.comment));
+ package_manifest
+ git_package_manifest (parser& p, bool iu)
+ {
+ package_manifest r (git_package_manifest (p, p.next (), iu));
- if (location)
- s.next ("location", location->posix_string ());
+ // Make sure this is the end.
+ //
+ name_value nv (p.next ());
+ if (!nv.empty ())
+ throw parsing (p.name (), nv.name_line, nv.name_column,
+ "single package manifest expected");
- if (sha256sum)
- s.next ("sha256sum", *sha256sum);
+ return r;
+ }
+
+ void
+ git_package_manifest (serializer& s, const package_manifest& m)
+ {
+ s.next ("", "1"); // Start of manifest.
+
+ auto bad_value ([&s](const string& d) {
+ throw serialization (s.name (), d);});
+
+ if (!m.location)
+ bad_value ("no valid location");
+
+ s.next ("location", m.location->posix_representation ());
s.next ("", ""); // End of manifest.
}
- // package_manifests
+ // bpkg_package_manifests
//
- package_manifests::
- package_manifests (parser& p, bool iu)
+ bpkg_package_manifests::
+ bpkg_package_manifests (parser& p, bool iu)
{
name_value nv (p.next ());
@@ -1367,10 +1472,10 @@ namespace bpkg
// Parse package manifests.
//
for (nv = p.next (); !nv.empty (); nv = p.next ())
- push_back (package_manifest (p, nv, iu));
+ push_back (bpkg_package_manifest (p, nv, iu));
}
- void package_manifests::
+ void bpkg_package_manifests::
serialize (serializer& s) const
{
// Serialize the package list manifest.
@@ -1386,11 +1491,10 @@ namespace bpkg
for (const package_manifest& p: *this)
{
auto bad_value = [&p, &s](const string& d)
- {
- throw
- serialization (
- s.name (), d + " for " + p.name + "-" + p.version.string ());
- };
+ {
+ throw serialization (
+ s.name (), d + " for " + p.name + "-" + p.version.string ());
+ };
if (p.description && p.description->file)
bad_value ("forbidden description-file");
@@ -1405,12 +1509,34 @@ namespace bpkg
if (!p.sha256sum)
bad_value ("no valid sha256sum");
- p.serialize (s);
+ bpkg_package_manifest (s, p);
}
s.next ("", ""); // End of stream.
}
+ // git_package_manifests
+ //
+ git_package_manifests::
+ git_package_manifests (parser& p, bool iu)
+ {
+ // Parse package manifests.
+ //
+ for (name_value nv (p.next ()); !nv.empty (); nv = p.next ())
+ push_back (git_package_manifest (p, nv, iu));
+ }
+
+ void git_package_manifests::
+ serialize (serializer& s) const
+ {
+ // Serialize package manifests.
+ //
+ for (const package_manifest& p: *this)
+ git_package_manifest (s, p);
+
+ s.next ("", ""); // End of stream.
+ }
+
// repository_url_traits
//
repository_url_traits::scheme_type repository_url_traits::
@@ -1419,7 +1545,7 @@ namespace bpkg
optional<authority_type>& authority,
optional<path_type>& path,
optional<string_type>& query,
- optional<string_type>& /*fragment*/)
+ optional<string_type>& fragment)
{
auto bad_url = [] (const char* d = "invalid URL")
{
@@ -1531,7 +1657,15 @@ namespace bpkg
{
try
{
- path = path_type (url).normalize ();
+ size_t p (url.find ('#'));
+
+ if (p != string::npos)
+ {
+ path = path_type (url.substr (0, p)).normalize ();
+ fragment = url.substr (p + 1); // Note: set after path normalization.
+ }
+ else
+ path = path_type (url).normalize ();
}
catch (const invalid_path&)
{
@@ -1567,6 +1701,15 @@ namespace bpkg
return "file";
url = path->relative () ? path->posix_string () : path->string ();
+
+ if (fragment)
+ {
+ assert (path->relative ());
+
+ url += '#';
+ url += *fragment;
+ }
+
return string_type ();
}
}
@@ -1634,6 +1777,36 @@ namespace bpkg
else throw invalid_argument ("invalid repository type '" + t + "'");
}
+ repository_type
+ guess_type (const repository_url& url, bool local)
+ {
+ switch (url.scheme)
+ {
+ case repository_protocol::git:
+ {
+ return repository_type::git;
+ }
+ case repository_protocol::http:
+ case repository_protocol::https:
+ {
+ return url.path->extension () == "git"
+ ? repository_type::git
+ : repository_type::bpkg;
+ }
+ case repository_protocol::file:
+ {
+ return local &&
+ dir_exists (path_cast<dir_path> (*url.path) / dir_path (".git"),
+ false)
+ ? repository_type::git
+ : repository_type::bpkg;
+ }
+ }
+
+ assert (false); // Can't be here.
+ return repository_type::bpkg;
+ }
+
// repository_location
//
static string
@@ -1777,20 +1950,10 @@ namespace bpkg
}
}
- // Base repository location is only meaningful for bpkg repository, and
- // can not be a relative path.
+ // Base repository location can not be a relative path.
//
- if (!b.empty ())
- {
- if (type_ != repository_type::bpkg)
- throw invalid_argument ("unexpected base location");
-
- if (b.type () != repository_type::bpkg)
- throw invalid_argument ("invalid base location");
-
- if (b.relative ())
- throw invalid_argument ("base location is relative filesystem path");
- }
+ if (!b.empty () && b.relative ())
+ throw invalid_argument ("base location is relative filesystem path");
path& up (*url_.path);
@@ -1846,6 +2009,10 @@ namespace bpkg
repository_url u (b.url ());
*u.path /= up;
+ // Override the base repository fragment.
+ //
+ u.fragment = move (url_.fragment);
+
url_ = move (u);
// Set canonical name to the base location canonical name 'bpkg:<host>'
@@ -1988,20 +2155,106 @@ namespace bpkg
// repository_manifest
//
- repository_manifest::
- repository_manifest (parser& p, bool iu)
- : repository_manifest (p, p.next (), iu) // Delegate
+ repository_role repository_manifest::
+ effective_role () const
{
- // Make sure this is the end.
+ if (role)
+ {
+ if (location.empty () != (*role == repository_role::base))
+ throw logic_error ("invalid role");
+
+ return *role;
+ }
+ else
+ return location.empty ()
+ ? repository_role::base
+ : repository_role::prerequisite;
+ }
+
+ optional<string> repository_manifest::
+ effective_url (const repository_location& l) const
+ {
+ static const char* invalid_location ("invalid repository location");
+
+ if (l.local ())
+ throw invalid_argument (invalid_location);
+
+ if (l.type () != repository_type::bpkg || !url || (*url)[0] != '.')
+ return url;
+
+ const path rp (*url);
+ auto i (rp.begin ());
+
+ static const char* invalid_url ("invalid relative url");
+
+ auto strip = [&i, &rp]() -> bool
+ {
+ if (i != rp.end ())
+ {
+ const auto& c (*i++);
+ if (c == "..")
+ return true;
+
+ if (c == ".")
+ return false;
+ }
+
+ throw invalid_argument (invalid_url);
+ };
+
+ bool strip_d (strip ()); // Strip domain.
+ bool strip_p (strip ()); // Strip path.
+
+ // The web interface relative path with the special first two components
+ // stripped.
//
- name_value nv (p.next ());
- if (!nv.empty ())
- throw parsing (p.name (), nv.name_line, nv.name_column,
- "single repository manifest expected");
+ const path rpath (i, rp.end ());
+ assert (rpath.relative ());
+
+ repository_url u (l.url ());
+
+ if (strip_d)
+ u.authority->host.value = strip_domain (u.authority->host,
+ repository_type::bpkg);
+
+ // Web interface URL path part.
+ //
+ // It is important to call strip_path() before appending the relative
+ // path. Otherwise the effective URL for the path ./../../.. and the
+ // repository location http://a.com/foo/pkg/1/math will wrongly be
+ // http://a.com/foo/pkg instead of http://a.com.
+ //
+ path ipath (strip_path (*u.path,
+ strip_p
+ ? strip_mode::component
+ : strip_mode::version) /
+ rpath);
+
+ try
+ {
+ ipath.normalize (false /* actual */, true /* cur_empty */);
+ }
+ catch (const invalid_path&)
+ {
+ throw invalid_argument (invalid_location);
+ }
+
+ assert (ipath.relative ());
+
+ if (!ipath.empty () && *ipath.begin () == "..")
+ throw invalid_argument (invalid_location);
+
+ // Strip the trailing slash for an empty path.
+ //
+ u.path = !ipath.empty () ? move (ipath) : optional<path> ();
+ return u.string ();
}
- repository_manifest::
- repository_manifest (parser& p, name_value nv, bool iu)
+ static repository_manifest
+ parse_repository_manifest (parser& p,
+ name_value nv,
+ repository_type base_type,
+ bool iu)
{
auto bad_name ([&p, &nv](const string& d) {
throw parsing (p.name (), nv.name_line, nv.name_column, d);});
@@ -2017,6 +2270,15 @@ namespace bpkg
if (nv.value != "1")
bad_value ("unsupported format version");
+ repository_manifest r;
+
+ // The repository type value can go after the location value. So we need to
+ // postpone the location value parsing until we went though all other
+ // values.
+ //
+ optional<repository_type> type;
+ optional<name_value> location;
+
for (nv = p.next (); !nv.empty (); nv = p.next ())
{
string& n (nv.name);
@@ -2024,19 +2286,22 @@ namespace bpkg
if (n == "location")
{
- if (!location.empty ())
+ if (location)
bad_name ("location redefinition");
if (v.empty ())
bad_value ("empty location");
+ location = move (nv);
+ }
+ else if (n == "type")
+ {
+ if (type)
+ bad_name ("type redefinition");
+
try
{
- // Call prerequisite repository location constructor, do not
- // ammend relative path.
- //
- location = repository_location (repository_url (move (v)),
- repository_location ());
+ type = to_repository_type (v);
}
catch (const invalid_argument& e)
{
@@ -2045,7 +2310,7 @@ namespace bpkg
}
else if (n == "role")
{
- if (role)
+ if (r.role)
bad_name ("role redefinition");
auto b (repository_role_names.cbegin ());
@@ -2055,21 +2320,21 @@ namespace bpkg
if (i == e)
bad_value ("unrecognized role");
- role = static_cast<repository_role> (i - b);
+ r.role = static_cast<repository_role> (i - b);
}
else if (n == "url")
{
- if (url)
+ if (r.url)
bad_name ("url redefinition");
if (v.empty ())
bad_value ("empty url");
- url = move (v);
+ r.url = move (v);
}
else if (n == "email")
{
- if (email)
+ if (r.email)
bad_name ("email redefinition");
auto vc (parser::split_comment (v));
@@ -2077,67 +2342,99 @@ namespace bpkg
if (vc.first.empty ())
bad_value ("empty email");
- email = email_type (move (vc.first), move (vc.second));
+ r.email = email (move (vc.first), move (vc.second));
}
else if (n == "summary")
{
- if (summary)
+ if (r.summary)
bad_name ("summary redefinition");
if (v.empty ())
bad_value ("empty summary");
- summary = move (v);
+ r.summary = move (v);
}
else if (n == "description")
{
- if (description)
+ if (r.description)
bad_name ("description redefinition");
if (v.empty ())
bad_value ("empty description");
- description = move (v);
+ r.description = move (v);
}
else if (n == "certificate")
{
- if (certificate)
+ if (base_type != repository_type::bpkg)
+ bad_name ("certificate not allowed");
+
+ if (r.certificate)
bad_name ("certificate redefinition");
if (v.empty ())
bad_value ("empty certificate");
- certificate = move (v);
+ r.certificate = move (v);
}
else if (!iu)
bad_name ("unknown name '" + n + "' in repository manifest");
}
- // Verify all non-optional values were specified.
+ // Parse location.
+ //
+ if (location)
+ try
+ {
+ repository_url u (move (location->value));
+
+ // If the prerequisite repository type is not specified explicitly then
+ // we consider it to be the base repository type for the relative
+ // location or guess it otherwise.
+ //
+ if (!type)
+ type = u.scheme == repository_protocol::file && u.path->relative ()
+ ? base_type
+ : guess_type (u, false); // Can't throw.
+
+ // Call prerequisite repository location constructor, do not amend
+ // relative path.
+ //
+ r.location = repository_location (u, *type, repository_location ());
+ }
+ catch (const invalid_argument& e)
+ {
+ nv = move (*location); // Restore as bad_value() uses its line/column.
+ bad_value (e.what ());
+ }
+
+ // Verify that all non-optional values were specified.
//
// - location can be omitted
// - role can be omitted
//
- if (role && location.empty () != (*role == repository_role::base))
+ if (r.role && r.location.empty () != (*r.role == repository_role::base))
bad_value ("invalid role");
- if (effective_role () != repository_role::base)
+ if (r.effective_role () != repository_role::base)
{
- if (url)
+ if (r.url)
bad_value ("url not allowed");
- if (email)
+ if (r.email)
bad_value ("email not allowed");
- if (summary)
+ if (r.summary)
bad_value ("summary not allowed");
- if (description)
+ if (r.description)
bad_value ("description not allowed");
- if (certificate)
+ if (r.certificate)
bad_value ("certificate not allowed");
}
+
+ return r;
}
void repository_manifest::
@@ -2150,10 +2447,8 @@ namespace bpkg
if (!location.empty ())
{
- if (location.remote () && location.type () != repository_type::bpkg)
- bad_value ("invalid repository location");
-
s.next ("location", location.string ());
+ s.next ("type", to_string (location.type ()));
}
if (role)
@@ -2211,110 +2506,99 @@ namespace bpkg
s.next ("", ""); // End of manifest.
}
- repository_role repository_manifest::
- effective_role () const
+ // bpkg_repository_manifest
+ //
+ repository_manifest
+ bpkg_repository_manifest (parser& p, bool iu)
{
- if (role)
- {
- if (location.empty () != (*role == repository_role::base))
- throw logic_error ("invalid role");
+ repository_manifest r (bpkg_repository_manifest (p, p.next (), iu));
- return *role;
- }
- else
- return location.empty ()
- ? repository_role::base
- : repository_role::prerequisite;
+ // Make sure this is the end.
+ //
+ name_value nv (p.next ());
+ if (!nv.empty ())
+ throw parsing (p.name (), nv.name_line, nv.name_column,
+ "single repository manifest expected");
+
+ return r;
}
- optional<string> repository_manifest::
- effective_url (const repository_location& l) const
+ repository_manifest
+ bpkg_repository_manifest (parser& p, name_value nv, bool iu)
{
- static const char* invalid_location ("invalid repository location");
-
- if (l.local ())
- throw invalid_argument (invalid_location);
-
- if (l.type () != repository_type::bpkg || !url || (*url)[0] != '.')
- return url;
-
- const path rp (*url);
- auto i (rp.begin ());
-
- static const char* invalid_url ("invalid relative url");
-
- auto strip = [&i, &rp]() -> bool
- {
- if (i != rp.end ())
- {
- const auto& c (*i++);
- if (c == "..")
- return true;
-
- if (c == ".")
- return false;
- }
-
- throw invalid_argument (invalid_url);
- };
+ return parse_repository_manifest (p, nv, repository_type::bpkg, iu);
+ }
- bool strip_d (strip ()); // Strip domain.
- bool strip_p (strip ()); // Strip path.
+ // git_repository_manifest
+ //
+ repository_manifest
+ git_repository_manifest (parser& p, bool iu)
+ {
+ repository_manifest r (git_repository_manifest (p, p.next (), iu));
- // The web interface relative path with the special first two components
- // stripped.
+ // Make sure this is the end.
//
- const path rpath (i, rp.end ());
- assert (rpath.relative ());
-
- repository_url u (l.url ());
+ name_value nv (p.next ());
+ if (!nv.empty ())
+ throw parsing (p.name (), nv.name_line, nv.name_column,
+ "single repository manifest expected");
- if (strip_d)
- u.authority->host.value = strip_domain (u.authority->host,
- repository_type::bpkg);
+ return r;
+ }
- // Web interface URL path part.
- //
- // It is important to call strip_path() before appending the relative
- // path. Otherwise the effective URL for the path ./../../.. and the
- // repository location http://a.com/foo/pkg/1/math will wrongly be
- // http://a.com/foo/pkg instead of http://a.com.
- //
- path ipath (strip_path (*u.path,
- strip_p
- ? strip_mode::component
- : strip_mode::version) /
- rpath);
+ repository_manifest
+ git_repository_manifest (parser& p, name_value nv, bool iu)
+ {
+ return parse_repository_manifest (p, nv, repository_type::git, iu);
+ }
- try
- {
- ipath.normalize (false /* actual */, true /* cur_empty */);
- }
- catch (const invalid_path&)
+ // bpkg_repository_manifests
+ //
+ bpkg_repository_manifests::
+ bpkg_repository_manifests (parser& p, bool iu)
+ {
+ name_value nv (p.next ());
+ while (!nv.empty ())
{
- throw invalid_argument (invalid_location);
+ push_back (bpkg_repository_manifest (p, nv, iu));
+ nv = p.next ();
+
+ // Make sure there is location in all except the last entry.
+ //
+ if (back ().location.empty () && !nv.empty ())
+ throw parsing (p.name (), nv.name_line, nv.name_column,
+ "repository location expected");
}
- assert (ipath.relative ());
+ if (empty () || !back ().location.empty ())
+ throw parsing (p.name (), nv.name_line, nv.name_column,
+ "base repository manifest expected");
+ }
- if (!ipath.empty () && *ipath.begin () == "..")
- throw invalid_argument (invalid_location);
+ void bpkg_repository_manifests::
+ serialize (serializer& s) const
+ {
+ if (empty () || !back ().location.empty ())
+ throw serialization (s.name (), "base repository manifest expected");
- // Strip the trailing slash for an empty path.
+ // @@ Should we check that there is location in all except the last
+ // entry?
//
- u.path = !ipath.empty () ? move (ipath) : optional<path> ();
- return u.string ();
+ for (const repository_manifest& r: *this)
+ r.serialize (s);
+
+ s.next ("", ""); // End of stream.
}
- // repository_manifests
+ // git_repository_manifests
//
- repository_manifests::
- repository_manifests (parser& p, bool iu)
+ git_repository_manifests::
+ git_repository_manifests (parser& p, bool iu)
{
name_value nv (p.next ());
while (!nv.empty ())
{
- push_back (repository_manifest (p, nv, iu));
+ push_back (git_repository_manifest (p, nv, iu));
nv = p.next ();
// Make sure there is location in all except the last entry.
@@ -2329,7 +2613,7 @@ namespace bpkg
"base repository manifest expected");
}
- void repository_manifests::
+ void git_repository_manifests::
serialize (serializer& s) const
{
if (empty () || !back ().location.empty ())
diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx
index 9d19b2c..a4721d7 100644
--- a/libbpkg/manifest.hxx
+++ b/libbpkg/manifest.hxx
@@ -385,27 +385,47 @@ namespace bpkg
public:
package_manifest () = default; // VC export.
- // Create individual package manifest.
- //
- package_manifest (butl::manifest_parser&, bool ignore_unknown = false);
-
- // Create an element of the package list manifest.
- //
- package_manifest (butl::manifest_parser&,
- butl::manifest_name_value start,
- bool ignore_unknown = false);
-
void
serialize (butl::manifest_serializer&) const;
-
- private:
- package_manifest (butl::manifest_parser&,
- butl::manifest_name_value start,
- bool in_list,
- bool ignore_unknown);
};
- class LIBBPKG_EXPORT package_manifests: public std::vector<package_manifest>
+ // Create individual package manifest.
+ //
+ LIBBPKG_EXPORT package_manifest
+ bpkg_package_manifest (butl::manifest_parser&, bool ignore_unknown = false);
+
+ LIBBPKG_EXPORT package_manifest
+ git_package_manifest (butl::manifest_parser&, bool ignore_unknown = false);
+
+ // Create an element of the package list manifest.
+ //
+ LIBBPKG_EXPORT package_manifest
+ bpkg_package_manifest (butl::manifest_parser&,
+ butl::manifest_name_value start,
+ bool ignore_unknown = false);
+
+ LIBBPKG_EXPORT package_manifest
+ git_package_manifest (butl::manifest_parser&,
+ butl::manifest_name_value start,
+ bool ignore_unknown = false);
+
+ // Serialize.
+ //
+ inline void
+ bpkg_package_manifest (butl::manifest_serializer& s,
+ const package_manifest& m)
+ {
+ m.serialize (s);
+ }
+
+ // Normally there is no need to serialize git package manifest, unless for
+ // testing.
+ //
+ LIBBPKG_EXPORT void
+ git_package_manifest (butl::manifest_serializer&, const package_manifest&);
+
+ class LIBBPKG_EXPORT bpkg_package_manifests:
+ public std::vector<package_manifest>
{
public:
using base_type = std::vector<package_manifest>;
@@ -417,9 +437,30 @@ namespace bpkg
std::string sha256sum;
public:
- package_manifests () = default;
- package_manifests (butl::manifest_parser&, bool ignore_unknown = false);
+ bpkg_package_manifests () = default;
+ bpkg_package_manifests (butl::manifest_parser&,
+ bool ignore_unknown = false);
+
+ void
+ serialize (butl::manifest_serializer&) const;
+ };
+
+ class LIBBPKG_EXPORT git_package_manifests:
+ public std::vector<package_manifest>
+ {
+ public:
+ using base_type = std::vector<package_manifest>;
+ using base_type::base_type;
+
+ public:
+ git_package_manifests () = default;
+ git_package_manifests (butl::manifest_parser&,
+ bool ignore_unknown = false);
+
+ // Normally there is no need to serialize git package manifests, unless for
+ // testing.
+ //
void
serialize (butl::manifest_serializer&) const;
};
@@ -444,7 +485,6 @@ namespace bpkg
butl::optional<string_type>&,
butl::optional<string_type>&);
-
static string_type
translate_scheme (string_type&,
const scheme_type&,
@@ -475,10 +515,9 @@ namespace bpkg
// not supported) and the path is relative.
//
// - For the local URL object the path can be relative or absolute. Query
- // can not be present. Fragment can not be present for the relative path
- // as there is no notation that can be used to represent it. Represent
- // the object as a local path if it is absolute and there is no fragment or
- // authority present.
+ // can not be present. Represent the object using the file:// notation if
+ // it is absolute and the authority or fragment is present. Otherwise
+ // represent it as a local path, appending the fragment if present.
//
using repository_url = butl::basic_url<repository_protocol,
repository_url_traits>;
@@ -499,6 +538,22 @@ namespace bpkg
return os << to_string (t);
}
+ // Guess the repository type for the URL:
+ //
+ // 1. If scheme is git then git.
+ //
+ // 2. If scheme is http(s), then check if path has the .git extension,
+ // then git, otherwise bpkg.
+ //
+ // 3. If local (which will normally be without the .git extension), check
+ // if directory contains the .git/ subdirectory then git, otherwise
+ // bpkg.
+ //
+ // Can throw system_error in the later case.
+ //
+ LIBBPKG_EXPORT repository_type
+ guess_type (const repository_url&, bool local);
+
class LIBBPKG_EXPORT repository_location
{
public:
@@ -538,9 +593,9 @@ namespace bpkg
// is empty, base itself is relative, or the resulting completed location
// is invalid.
//
- repository_location (repository_url u,
- const repository_location& base)
- : repository_location (std::move (u), repository_type::bpkg, base) {}
+ repository_location (repository_url,
+ repository_type,
+ const repository_location& base);
repository_location (const repository_location& l,
const repository_location& base)
@@ -669,13 +724,6 @@ namespace bpkg
}
private:
- // Used for delegating in public constructor.
- //
- repository_location (repository_url,
- repository_type,
- const repository_location& base);
-
- private:
std::string canonical_name_;
repository_url url_;
repository_type type_;
@@ -757,16 +805,50 @@ namespace bpkg
public:
repository_manifest () = default; // VC export.
- repository_manifest (butl::manifest_parser&, bool ignore_unknown = false);
- repository_manifest (butl::manifest_parser&,
- butl::manifest_name_value start,
- bool ignore_unknown = false);
void
serialize (butl::manifest_serializer&) const;
};
- class LIBBPKG_EXPORT repository_manifests:
+ // Create individual repository manifest.
+ //
+ LIBBPKG_EXPORT repository_manifest
+ bpkg_repository_manifest (butl::manifest_parser&,
+ bool ignore_unknown = false);
+
+ LIBBPKG_EXPORT repository_manifest
+ git_repository_manifest (butl::manifest_parser&,
+ bool ignore_unknown = false);
+
+ // Create an element of the repository list manifest.
+ //
+ LIBBPKG_EXPORT repository_manifest
+ bpkg_repository_manifest (butl::manifest_parser&,
+ butl::manifest_name_value start,
+ bool ignore_unknown = false);
+
+ LIBBPKG_EXPORT repository_manifest
+ git_repository_manifest (butl::manifest_parser&,
+ butl::manifest_name_value start,
+ bool ignore_unknown = false);
+
+ class LIBBPKG_EXPORT bpkg_repository_manifests:
+ public std::vector<repository_manifest>
+ {
+ public:
+ using base_type = std::vector<repository_manifest>;
+
+ using base_type::base_type;
+
+ bpkg_repository_manifests () = default;
+ bpkg_repository_manifests (butl::manifest_parser&,
+ bool ignore_unknown = false);
+
+ void
+ serialize (butl::manifest_serializer&) const;
+ };
+
+ class LIBBPKG_EXPORT git_repository_manifests:
public std::vector<repository_manifest>
{
public:
@@ -774,8 +856,9 @@ namespace bpkg
using base_type::base_type;
- repository_manifests () = default;
- repository_manifests (butl::manifest_parser&, bool ignore_unknown = false);
+ git_repository_manifests () = default;
+ git_repository_manifests (butl::manifest_parser&,
+ bool ignore_unknown = false);
void
serialize (butl::manifest_serializer&) const;
diff --git a/tests/manifest/driver.cxx b/tests/manifest/driver.cxx
index b7a87eb..9c58dcf 100644
--- a/tests/manifest/driver.cxx
+++ b/tests/manifest/driver.cxx
@@ -21,8 +21,10 @@ using namespace bpkg;
// Read and parse manifest from STDIN and serialize it to STDOUT. The
// following options specify the manifest type.
//
-// -p parse package manifest list
-// -r parse repository manifest list
+// -bp parse bpkg package manifest list
+// -gp parse git package manifest list
+// -br parse bpkg repository manifest list
+// -gr parse git repository manifest list
// -s parse signature manifest
//
int
@@ -37,10 +39,14 @@ main (int argc, char* argv[])
manifest_parser p (cin, "stdin");
manifest_serializer s (cout, "stdout");
- if (opt == "-p")
- package_manifests (p).serialize (s);
- else if (opt == "-r")
- repository_manifests (p).serialize (s);
+ if (opt == "-bp")
+ bpkg_package_manifests (p).serialize (s);
+ else if (opt == "-br")
+ bpkg_repository_manifests (p).serialize (s);
+ else if (opt == "-gp")
+ git_package_manifests (p).serialize (s);
+ else if (opt == "-gr")
+ git_repository_manifests (p).serialize (s);
else if (opt == "-s")
signature_manifest (p).serialize (s);
else
diff --git a/tests/manifest/testscript b/tests/manifest/testscript
index 1b0f38f..3f0d38d 100644
--- a/tests/manifest/testscript
+++ b/tests/manifest/testscript
@@ -4,128 +4,262 @@
: packages
:
-: Roundtrip the package manifest list.
-:
{
- : manifest
- :
- $* -p <<EOF >>EOF
- : 1
- sha256sum: a2b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+ : bpkg
:
- name: libfoo
- version: 1.2.3+2
- priority: high; Due to critical bug fix.
- summary: Modern XML parser
- license: LGPLv2, MIT; Both required.
- license: BSD
- tags: c++, xml, parser, serializer, pull, streaming, modern
- description: libfoo is a very modern C++ XML parser.
- changes: 1.2.3+2: applied upstream patch for critical bug bar
- changes: 1.2.3+1: applied upstream patch for critical bug foo
- url: http://www.example.org/projects/libfoo/; libfoo project page url
- doc-url: http://www.example.org/projects/libfoo/man.xhtml; documentation page
- src-url: http://scm.example.com/?p=odb/libodb.git\;a=tree; source tree
- package-url: http://www.example.org/projects/libfoo/1.2.3+2; package url
- email: libfoo-users@example.org; Public mailing list, posts by non-members\
- are allowed but moderated.
- package-email: libfoo-1.2.3+2@example.org; Bug reports are welcome.
- build-email: libfoo-builds@example.org; Mailing list for bbot notification\
- emails.
- depends: libz
- depends: libgnutls <= 1.2.3 | libopenssl >= 2.3.4
- depends: ? libboost-regex >= 1.52.0; Only if C++ compiler does not support\
- C++11 <regex>.
- depends: ? libqtcore >= 5.0.0; Only if GUI is enabled.
- requires: linux | windows | macosx; symbian is coming.
- requires: c++11
- requires: ? ; VC++ 12.0 or later if targeting Windows.
- requires: ? ; libc++ standard library if using Clang on Mac OS X.
- requires: zlib; Most Linux/UNIX systems already have one; or get it at\
- www.zlib.net.
- build-include: linux*
- build-include: freebsd*
- build-exclude: *; Only supports Linux and FreeBSD.
- location: libfoo-1.2.3+2.tar.bz2
- sha256sum: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
- :
- name: libbar
- version: 3.4A.5+6
- summary: Modern bar management framework
- license: LGPLv2
- tags: c++, xml, modern
- url: http://www.example.org/projects/libbar/
- email: libbar-users@example.org
- build-email:
- depends: libbaz (1- 2-) | libbaz [3 4-) | libbaz (5 6] | libbaz [7 8]
- build-exclude: *-msvc_14*/i?86-*; Linker crash.
- location: bar/libbar-3.4A.5+6.tbz
- sha256sum: d4b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+ {
+ : manifest
+ :
+ : Roundtrip the bpkg package manifest list.
+ :
+ $* -bp <<EOF >>EOF
+ : 1
+ sha256sum: a2b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+ :
+ name: libfoo
+ version: 1.2.3+2
+ priority: high; Due to critical bug fix.
+ summary: Modern XML parser
+ license: LGPLv2, MIT; Both required.
+ license: BSD
+ tags: c++, xml, parser, serializer, pull, streaming, modern
+ description: libfoo is a very modern C++ XML parser.
+ changes: 1.2.3+2: applied upstream patch for critical bug bar
+ changes: 1.2.3+1: applied upstream patch for critical bug foo
+ url: http://www.example.org/projects/libfoo/; libfoo project page url
+ doc-url: http://www.example.org/projects/libfoo/man.xhtml; documentation page
+ src-url: http://scm.example.com/?p=odb/libodb.git\;a=tree; source tree
+ package-url: http://www.example.org/projects/libfoo/1.2.3+2; package url
+ email: libfoo-users@example.org; Public mailing list, posts by non-members\
+ are allowed but moderated.
+ package-email: libfoo-1.2.3+2@example.org; Bug reports are welcome.
+ build-email: libfoo-builds@example.org; Mailing list for bbot notification\
+ emails.
+ depends: libz
+ depends: libgnutls <= 1.2.3 | libopenssl >= 2.3.4
+ depends: ? libboost-regex >= 1.52.0; Only if C++ compiler does not support\
+ C++11 <regex>.
+ depends: ? libqtcore >= 5.0.0; Only if GUI is enabled.
+ requires: linux | windows | macosx; symbian is coming.
+ requires: c++11
+ requires: ? ; VC++ 12.0 or later if targeting Windows.
+ requires: ? ; libc++ standard library if using Clang on Mac OS X.
+ requires: zlib; Most Linux/UNIX systems already have one; or get it at\
+ www.zlib.net.
+ build-include: linux*
+ build-include: freebsd*
+ build-exclude: *; Only supports Linux and FreeBSD.
+ location: libfoo-1.2.3+2.tar.bz2
+ sha256sum: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+ :
+ name: libbar
+ version: 3.4A.5+6
+ summary: Modern bar management framework
+ license: LGPLv2
+ tags: c++, xml, modern
+ url: http://www.example.org/projects/libbar/
+ email: libbar-users@example.org
+ build-email:
+ depends: libbaz (1- 2-) | libbaz [3 4-) | libbaz (5 6] | libbaz [7 8]
+ build-exclude: *-msvc_14*/i?86-*; Linker crash.
+ location: bar/libbar-3.4A.5+6.tbz
+ sha256sum: d4b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+ :
+ name: libbaz
+ version: 2~3.4A.5+3
+ summary: Modern baz system
+ license: LGPLv2
+ url: http://www.example.org/projects/libbar/
+ email: libbaz-users@example.org
+ location: libbaz/libbaz-2~3.4A.5+3.tar.gz
+ sha256sum: b5b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+ EOF
+ }
+
+ : git
:
- name: libbaz
- version: 2~3.4A.5+3
- summary: Modern baz system
- license: LGPLv2
- url: http://www.example.org/projects/libbar/
- email: libbaz-users@example.org
- location: libbaz/libbaz-2~3.4A.5+3.tar.gz
- sha256sum: b5b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
- EOF
+ {
+ : manifest
+ :
+ : Roundtrip the git package manifest list.
+ :
+ $* -gp <<EOF >>EOF
+ : 1
+ location: hello/
+ :
+ location: mhello/
+ EOF
+
+ : empty
+ :
+ : Roundtrip an empty git package manifest list.
+ :
+ $* -gp <"" >:""
+ }
}
: repositories
:
-: Roundtrip the repository manifest list.
-:
{
- : manifest
+ : bpkg
:
- $* -r <<EOF >>EOF
- : 1
- location: http://pkg.example.org/1/math
- role: prerequisite
- :
- location: ../stable
- role: complement
+ {
+ : manifest
+ :
+ : Roundtrip the bpkg repository manifest list.
+ :
+ $* -br <<EOF >>EOF
+ : 1
+ location: http://pkg.example.org/1/math
+ type: bpkg
+ role: prerequisite
+ :
+ location: ../stable
+ type: bpkg
+ role: complement
+ :
+ url: http://cppget.org
+ email: repoman@cppget.org; General mailing list.
+ summary: General C++ package repository
+ description: This is the awesome C++ package repository full of exciting\
+ stuff.
+ certificate: \
+ -----BEGIN CERTIFICATE-----
+ MIIFLzCCAxegAwIBAgIJAJ71rMp8mDy1MA0GCSqGSIb3DQEBCwUAMDMxFzAVBgNV
+ BAoMDkNvZGUgU3ludGhlc2lzMRgwFgYDVQQDDA9uYW1lOmNwcGdldC5vcmcwHhcN
+ MTYwNDA4MTc1NTUwWhcNMTcwNDA4MTc1NTUwWjAzMRcwFQYDVQQKDA5Db2RlIFN5
+ bnRoZXNpczEYMBYGA1UEAwwPbmFtZTpjcHBnZXQub3JnMIICIjANBgkqhkiG9w0B
+ AQEFAAOCAg8AMIICCgKCAgEAwj7lwxkr19ygfNIzQsiKkmyyRG0c5AwMrwvldEk7
+ 2UIwz5kNb04zveUzQcfNFhau60+xC980Y4TKA4/ScfinyaDfp1I3pmiv4OSDUoBw
+ 9e8a+4Jyo5fuiAXoAYaQyAdwvH1mIbYq1ObRfKW2MTrUXp/HRJAWHHBnv3VmCYBZ
+ dllY1hasA+SBDMBv6iTXkKUIfEdNDk8cjUR3FjxaefIdip9pHR3G0y4iWctS1drq
+ AKLE1J0KIJyPsJCvoZnzIeePaCNL/UtRup9mYi2vxHHFD4Ml5Bbp+gE6vq5XhcQz
+ LeCcGYKB3UjVWuszcpFIoHACw9ja2JUumbTiclUDgLBk8WXJvLjOCNLp9i/MKQws
+ p5CDfrNe2P6u63ZmtW2v0Qpj/6b6JQmqJaMgHQdDEBUFO3bjwm7yyXyvEjj/EAEJ
+ pGziWZjan5NKGgKCX1JChQJloMHhzr42YMvceWTMJjAr07Es9vCsCS2KPvAKY7Mv
+ yewAyK9ucFRDObZVuaFjU+WUTXB1munwO3Jso56EMxeFvu+W1B+m49XS3k/TlBvF
+ HGnkiSaMwLEJvgFVgQPpG2gD39WDFqX28pWdLL4hM+hXUfdeH0OdXfq66CLu7P8d
+ cgkZdHRs5UauxLzm1Qo06aLsm2HXrfDnmsd5ENi7RkiFMx1aLh3/cjZD0uHndQUC
+ LEcCAwEAAaNGMEQwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUF
+ BwMDMBoGA1UdEQQTMBGBD2luZm9AY3BwZ2V0Lm9yZzANBgkqhkiG9w0BAQsFAAOC
+ AgEAHLfv2w82bBMgDgsRX8GU/3eK6CnyfRu4Auto1XjyHCrD6qcIdmebC0hihpSg
+ 5xSlfVwjPRWBmg3z5/K8ln5jM6KKiWHd47OCfx+DW7wbesq2+6lS1btXpRR2pv7j
+ zG+41Cncu/xVNs9F4CQluVn5xyWFVDUxQfkQqAE46EbkjAmq42y+1ZQnq2Zm47Wr
+ iMRXQtg1yx7Fs2EpVU+sbW4ImuXgv0YbyYbI1lPhvmx8rIL6lybN3evEfIj7crh7
+ 5abWPDZzA+1aNL5tiaSNrn3nS/BfJyEYhGMyy0bsekPZiaqGB1q/mgv2rmR/2SRL
+ Tx+T7sthy/IHTOUbDTY0lUhjc6thQMncgGTaD4TC3QaXhdLWzO9XTh0K7U8BOMwh
+ wppr1G5aTXY0PUB0+Hs+IQZ4mVfBvKO0Wn6GgoDAs/mW9qvbWP3ZnpdvhB52a49P
+ g07JQ+R0QgBNQY7t0lT0mOpAPx79Dwc5R8jQCkx4gTr1bWtgyCvza+gpTgUQDOH5
+ nawOIIDOnRv4heFdvgfEQs2oKa3X4bM+BsgOx7OTvnWCzJy0IXo0uBbcTrMv9Z62
+ +KVwnghQdpURRnUpomt03cTwjqVJVrq287owGv8qqnuGcTTi1SgzNNYREFoljY58
+ CCj4yYvTUzXjcAUXaNC5YNw3JEQp8vmciuJwhyUkbifLrHU=
+ -----END CERTIFICATE-----
+ \
+ EOF
+
+ : prerequisite-type
+ :
+ $* -br <<EOI >>EOO
+ : 1
+ location: http://example.org/math.git#master
+ role: prerequisite
+ :
+ location: ../stable.git
+ role: complement
+ :
+ location: git://example.org/foo#master
+ type: git
+ role: prerequisite
+ :
+ location: http://pkg.example.org/1/bar
+ role: prerequisite
+ :
+ url: http://cppget.org
+ EOI
+ : 1
+ location: http://example.org/math.git#master
+ type: git
+ role: prerequisite
+ :
+ location: ../stable.git
+ type: bpkg
+ role: complement
+ :
+ location: git://example.org/foo#master
+ type: git
+ role: prerequisite
+ :
+ location: http://pkg.example.org/1/bar
+ type: bpkg
+ role: prerequisite
+ :
+ url: http://cppget.org
+ EOO
+ }
+
+ : git
:
- url: http://cppget.org
- email: repoman@cppget.org; General mailing list.
- summary: General C++ package repository
- description: This is the awesome C++ package repository full of exciting\
- stuff.
- certificate: \
- -----BEGIN CERTIFICATE-----
- MIIFLzCCAxegAwIBAgIJAJ71rMp8mDy1MA0GCSqGSIb3DQEBCwUAMDMxFzAVBgNV
- BAoMDkNvZGUgU3ludGhlc2lzMRgwFgYDVQQDDA9uYW1lOmNwcGdldC5vcmcwHhcN
- MTYwNDA4MTc1NTUwWhcNMTcwNDA4MTc1NTUwWjAzMRcwFQYDVQQKDA5Db2RlIFN5
- bnRoZXNpczEYMBYGA1UEAwwPbmFtZTpjcHBnZXQub3JnMIICIjANBgkqhkiG9w0B
- AQEFAAOCAg8AMIICCgKCAgEAwj7lwxkr19ygfNIzQsiKkmyyRG0c5AwMrwvldEk7
- 2UIwz5kNb04zveUzQcfNFhau60+xC980Y4TKA4/ScfinyaDfp1I3pmiv4OSDUoBw
- 9e8a+4Jyo5fuiAXoAYaQyAdwvH1mIbYq1ObRfKW2MTrUXp/HRJAWHHBnv3VmCYBZ
- dllY1hasA+SBDMBv6iTXkKUIfEdNDk8cjUR3FjxaefIdip9pHR3G0y4iWctS1drq
- AKLE1J0KIJyPsJCvoZnzIeePaCNL/UtRup9mYi2vxHHFD4Ml5Bbp+gE6vq5XhcQz
- LeCcGYKB3UjVWuszcpFIoHACw9ja2JUumbTiclUDgLBk8WXJvLjOCNLp9i/MKQws
- p5CDfrNe2P6u63ZmtW2v0Qpj/6b6JQmqJaMgHQdDEBUFO3bjwm7yyXyvEjj/EAEJ
- pGziWZjan5NKGgKCX1JChQJloMHhzr42YMvceWTMJjAr07Es9vCsCS2KPvAKY7Mv
- yewAyK9ucFRDObZVuaFjU+WUTXB1munwO3Jso56EMxeFvu+W1B+m49XS3k/TlBvF
- HGnkiSaMwLEJvgFVgQPpG2gD39WDFqX28pWdLL4hM+hXUfdeH0OdXfq66CLu7P8d
- cgkZdHRs5UauxLzm1Qo06aLsm2HXrfDnmsd5ENi7RkiFMx1aLh3/cjZD0uHndQUC
- LEcCAwEAAaNGMEQwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUF
- BwMDMBoGA1UdEQQTMBGBD2luZm9AY3BwZ2V0Lm9yZzANBgkqhkiG9w0BAQsFAAOC
- AgEAHLfv2w82bBMgDgsRX8GU/3eK6CnyfRu4Auto1XjyHCrD6qcIdmebC0hihpSg
- 5xSlfVwjPRWBmg3z5/K8ln5jM6KKiWHd47OCfx+DW7wbesq2+6lS1btXpRR2pv7j
- zG+41Cncu/xVNs9F4CQluVn5xyWFVDUxQfkQqAE46EbkjAmq42y+1ZQnq2Zm47Wr
- iMRXQtg1yx7Fs2EpVU+sbW4ImuXgv0YbyYbI1lPhvmx8rIL6lybN3evEfIj7crh7
- 5abWPDZzA+1aNL5tiaSNrn3nS/BfJyEYhGMyy0bsekPZiaqGB1q/mgv2rmR/2SRL
- Tx+T7sthy/IHTOUbDTY0lUhjc6thQMncgGTaD4TC3QaXhdLWzO9XTh0K7U8BOMwh
- wppr1G5aTXY0PUB0+Hs+IQZ4mVfBvKO0Wn6GgoDAs/mW9qvbWP3ZnpdvhB52a49P
- g07JQ+R0QgBNQY7t0lT0mOpAPx79Dwc5R8jQCkx4gTr1bWtgyCvza+gpTgUQDOH5
- nawOIIDOnRv4heFdvgfEQs2oKa3X4bM+BsgOx7OTvnWCzJy0IXo0uBbcTrMv9Z62
- +KVwnghQdpURRnUpomt03cTwjqVJVrq287owGv8qqnuGcTTi1SgzNNYREFoljY58
- CCj4yYvTUzXjcAUXaNC5YNw3JEQp8vmciuJwhyUkbifLrHU=
- -----END CERTIFICATE-----
- \
- EOF
+ {
+ : manifest
+ :
+ : Roundtrip the git repository manifest list.
+ :
+ $* -gr <<EOF >>EOF
+ : 1
+ location: http://example.org/math.git#master
+ type: git
+ role: prerequisite
+ :
+ location: ../stable.git#stable
+ type: git
+ role: complement
+ :
+ url: http://cppget.org
+ email: repoman@cppget.org; General mailing list.
+ summary: General C++ package repository
+ description: This is the awesome C++ package repository full of exciting\
+ stuff.
+ EOF
+
+ : prerequisite-type
+ :
+ $* -gr <<EOI >>EOO
+ : 1
+ location: http://example.org/math.git#master
+ role: prerequisite
+ :
+ location: ../stable.git#stable
+ role: complement
+ :
+ location: git://example.org/foo#master
+ type: git
+ role: prerequisite
+ :
+ location: http://pkg.example.org/1/bar
+ role: prerequisite
+ :
+ url: http://cppget.org
+ EOI
+ : 1
+ location: http://example.org/math.git#master
+ type: git
+ role: prerequisite
+ :
+ location: ../stable.git#stable
+ type: git
+ role: complement
+ :
+ location: git://example.org/foo#master
+ type: git
+ role: prerequisite
+ :
+ location: http://pkg.example.org/1/bar
+ type: bpkg
+ role: prerequisite
+ :
+ url: http://cppget.org
+ EOO
+ }
}
: signature
diff --git a/tests/repository-location/driver.cxx b/tests/repository-location/driver.cxx
index ad3b0dd..e4282fe 100644
--- a/tests/repository-location/driver.cxx
+++ b/tests/repository-location/driver.cxx
@@ -33,9 +33,11 @@ namespace bpkg
}
inline static repository_location
- loc (const string& l, const repository_location& b)
+ loc (const string& l,
+ const repository_location& b,
+ repository_type t = repository_type::bpkg)
{
- return repository_location (repository_url (l), b);
+ return repository_location (repository_url (l), t, b);
}
inline static bool
@@ -53,11 +55,13 @@ namespace bpkg
}
inline static bool
- bad_loc (const string& l, const repository_location& b)
+ bad_loc (const string& l,
+ const repository_location& b,
+ repository_type t = repository_type::bpkg)
{
try
{
- repository_location bl (repository_url (l), b);
+ repository_location bl (repository_url (l), t, b);
return false;
}
catch (const invalid_argument&)
@@ -71,7 +75,7 @@ namespace bpkg
{
istringstream is (":1\nurl: " + l);
manifest_parser mp (is, "");
- repository_manifest m (mp);
+ repository_manifest m (bpkg_repository_manifest (mp));
optional<string> u (m.effective_url (r));
assert (u);
@@ -307,6 +311,11 @@ namespace bpkg
assert (l.canonical_name () == "git:/git/repo");
}
{
+ repository_location l (loc ("/git/repo#branch", repository_type::git));
+ assert (l.string () == "file:/git/repo#branch");
+ assert (l.canonical_name () == "git:/git/repo");
+ }
+ {
repository_location l (loc ("file://localhost/#master",
repository_type::git));
assert (l.string () == "file:/#master");
@@ -368,6 +377,12 @@ namespace bpkg
assert (l.canonical_name () == "git:c:\\git\\repo");
}
{
+ repository_location l (loc ("c:\\git\\repo#branch",
+ repository_type::git));
+ assert (l.string () == "file:/c:/git/repo#branch");
+ assert (l.canonical_name () == "git:c:\\git\\repo");
+ }
+ {
repository_location l (loc ("file://localhost/c:/#master",
repository_type::git));
assert (l.string () == "file:/c:#master");
@@ -435,6 +450,7 @@ namespace bpkg
assert (l.string () == "http://example.com/test.git#master");
assert (l.canonical_name () == "git:example.com/test");
assert (l.proto () == proto::http);
+ assert (l.type () == repository_type::git);
}
{
repository_location l (loc ("https://example.com/test.git#master",
@@ -576,6 +592,24 @@ namespace bpkg
assert (l2.canonical_name () == "bpkg:stable.cppget.org:444/math");
assert (l2.proto () == proto::https);
}
+ {
+ repository_location l (loc ("../test.git#master",
+ repository_location (),
+ repository_type::git));
+ assert (l.string () == "../test.git#master");
+ assert (l.canonical_name ().empty ());
+ assert (l.proto () == proto::file);
+ }
+ {
+ repository_location l1 (loc ("https://example.com/stable.git#stable",
+ repository_type::git));
+ repository_location l2 (loc ("../test.git#master",
+ l1,
+ repository_type::git));
+ assert (l2.string () == "https://example.com/test.git#master");
+ assert (l2.canonical_name () == "git:example.com/test");
+ assert (l2.proto () == proto::https);
+ }
#ifndef _WIN32
{
repository_location l1 (loc ("/var/r1/1/misc"));