From 18e40e91c99088ef5fb9458ec4c1ec21ddcc51e6 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 14 Feb 2018 19:27:47 +0300 Subject: Add support for git manifests --- libbpkg/manifest.cxx | 896 +++++++++++++++++++++++------------ libbpkg/manifest.hxx | 165 +++++-- tests/manifest/driver.cxx | 18 +- tests/manifest/testscript | 358 +++++++++----- tests/repository-location/driver.cxx | 44 +- 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 (i - b), - move (vc.second)); + r.priority = + priority (static_cast (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 (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, optional& path, optional& query, - optional& /*fragment*/) + optional& 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 (*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:' @@ -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 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 (); + 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 type; + optional 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 (i - b); + r.role = static_cast (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 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 (); - 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 + // 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 { public: using base_type = std::vector; @@ -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 + { + public: + using base_type = std::vector; + 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&, butl::optional&); - 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; @@ -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 + { + public: + using base_type = std::vector; + + 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 { 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 - : 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 . - 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 + : 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 . + 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 + : 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 - : 1 - location: http://pkg.example.org/1/math - role: prerequisite - : - location: ../stable - role: complement + { + : manifest + : + : Roundtrip the bpkg repository manifest list. + : + $* -br <>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 <>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 + : 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 <>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 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")); -- cgit v1.1