aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libbpkg/manifest.cxx308
-rw-r--r--libbpkg/manifest.hxx100
-rw-r--r--tests/manifest/driver.cxx14
-rw-r--r--tests/manifest/testscript31
-rw-r--r--tests/repository-location/driver.cxx14
5 files changed, 370 insertions, 97 deletions
diff --git a/libbpkg/manifest.cxx b/libbpkg/manifest.cxx
index a030be2..d6a7a14 100644
--- a/libbpkg/manifest.cxx
+++ b/libbpkg/manifest.cxx
@@ -1327,10 +1327,11 @@ namespace bpkg
return pkg_package_manifest (p, nv, true, iu);
}
- // git_package_manifest
+ // Parse the directory manifest that may contain the only (and required)
+ // location name that refers to the package directory.
//
- package_manifest
- git_package_manifest (parser& p, name_value nv, bool iu)
+ static package_manifest
+ parse_directory_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);});
@@ -1391,10 +1392,10 @@ namespace bpkg
return r;
}
- package_manifest
- git_package_manifest (parser& p, bool iu)
+ static package_manifest
+ parse_directory_manifest (parser& p, bool iu)
{
- package_manifest r (git_package_manifest (p, p.next (), iu));
+ package_manifest r (parse_directory_manifest (p, p.next (), iu));
// Make sure this is the end.
//
@@ -1406,8 +1407,10 @@ namespace bpkg
return r;
}
- void
- git_package_manifest (serializer& s, const package_manifest& m)
+ // Serialize the directory manifest (see above).
+ //
+ static void
+ serialize_directory_manifest (serializer& s, const package_manifest& m)
{
s.next ("", "1"); // Start of manifest.
@@ -1422,6 +1425,46 @@ namespace bpkg
s.next ("", ""); // End of manifest.
}
+ // dir_package_manifest
+ //
+ package_manifest
+ dir_package_manifest (parser& p, name_value nv, bool iu)
+ {
+ return parse_directory_manifest (p, nv, iu);
+ }
+
+ package_manifest
+ dir_package_manifest (parser& p, bool iu)
+ {
+ return parse_directory_manifest (p, iu);
+ }
+
+ void
+ dir_package_manifest (serializer& s, const package_manifest& m)
+ {
+ serialize_directory_manifest (s, m);
+ }
+
+ // git_package_manifest
+ //
+ package_manifest
+ git_package_manifest (parser& p, name_value nv, bool iu)
+ {
+ return parse_directory_manifest (p, nv, iu);
+ }
+
+ package_manifest
+ git_package_manifest (parser& p, bool iu)
+ {
+ return parse_directory_manifest (p, iu);
+ }
+
+ void
+ git_package_manifest (serializer& s, const package_manifest& m)
+ {
+ serialize_directory_manifest (s, m);
+ }
+
// pkg_package_manifests
//
pkg_package_manifests::
@@ -1515,6 +1558,28 @@ namespace bpkg
s.next ("", ""); // End of stream.
}
+ // dir_package_manifests
+ //
+ dir_package_manifests::
+ dir_package_manifests (parser& p, bool iu)
+ {
+ // Parse package manifests.
+ //
+ for (name_value nv (p.next ()); !nv.empty (); nv = p.next ())
+ push_back (dir_package_manifest (p, nv, iu));
+ }
+
+ void dir_package_manifests::
+ serialize (serializer& s) const
+ {
+ // Serialize package manifests.
+ //
+ for (const package_manifest& p: *this)
+ dir_package_manifest (s, p);
+
+ s.next ("", ""); // End of stream.
+ }
+
// git_package_manifests
//
git_package_manifests::
@@ -1762,6 +1827,7 @@ namespace bpkg
switch (t)
{
case repository_type::pkg: return "pkg";
+ case repository_type::dir: return "dir";
case repository_type::git: return "git";
}
@@ -1773,6 +1839,7 @@ namespace bpkg
to_repository_type (const string& t)
{
if (t == "pkg") return repository_type::pkg;
+ else if (t == "dir") return repository_type::dir;
else if (t == "git") return repository_type::git;
else throw invalid_argument ("invalid repository type '" + t + "'");
}
@@ -1837,6 +1904,14 @@ namespace bpkg
break;
}
+ case repository_type::dir:
+ {
+ // Can't be here as repository location for the dir type can only be
+ // local.
+ //
+ assert (false);
+ break;
+ }
}
if (h && h->empty ())
@@ -1939,6 +2014,20 @@ namespace bpkg
if (url_.scheme == repository_protocol::git)
throw invalid_argument ("unsupported scheme for pkg repository");
+ if (url_.fragment)
+ throw invalid_argument ("unexpected fragment for pkg repository");
+
+ break;
+ }
+ case repository_type::dir:
+ {
+ if (url_.scheme != repository_protocol::file)
+ throw invalid_argument (
+ "unsupported scheme for dir repository");
+
+ if (url_.fragment)
+ throw invalid_argument ("unexpected fragment for dir repository");
+
break;
}
case repository_type::git:
@@ -1963,6 +2052,7 @@ namespace bpkg
switch (t)
{
case repository_type::pkg:
+ case repository_type::dir:
case repository_type::git:
{
if (!up.to_directory ())
@@ -2066,27 +2156,48 @@ namespace bpkg
return;
}
- // Canonical name <prefix>/<path> part for the pkg repository, and the
- // full path with the .git extension stripped for the git repository.
+ // Canonical name part that is produced from the repository location path
+ // part. The algorithm depends on the repository type.
//
path sp;
- if (type_ == repository_type::pkg)
+ switch (type_)
{
- sp = strip_path (up,
- remote ()
- ? strip_mode::component
- : strip_mode::path);
+ case repository_type::pkg:
+ {
+ // Produce the pkg repository canonical name <prefix>/<path> part (see
+ // the Repository Chaining documentation for more details).
+ //
+ sp = strip_path (up,
+ remote ()
+ ? strip_mode::component
+ : strip_mode::path);
+
+ // If for an absolute path location the stripping result is empty
+ // (which also means <path> part is empty as well) then fallback to
+ // stripping just the version component.
+ //
+ if (absolute () && sp.empty ())
+ sp = strip_path (up, strip_mode::version);
- // If for an absolute path location the stripping result is empty (which
- // also means <path> part is empty as well) then fallback to stripping
- // just the version component.
- //
- if (absolute () && sp.empty ())
- sp = strip_path (up, strip_mode::version);
+ break;
+ }
+ case repository_type::dir:
+ {
+ // For dir repository we use the absolute (normalized) path.
+ //
+ sp = up;
+ break;
+ }
+ case repository_type::git:
+ {
+ // For git repository we use the absolute (normalized) path, stripping
+ // the .git extension if present.
+ //
+ sp = strip_path (up, strip_mode::extension);
+ break;
+ }
}
- else
- sp = strip_path (up, strip_mode::extension);
string cp (sp.relative () ? sp.posix_string () : sp.string ());
@@ -2401,9 +2512,20 @@ namespace bpkg
// 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.
+ {
+ if (u.scheme == repository_protocol::file && u.path->relative ())
+ {
+ type = base_type;
+
+ // Strip the URL fragment if the base repository type is dir (see
+ // the Repository Manifest documentation for the gory details).
+ //
+ if (base_type == repository_type::dir)
+ u.fragment = nullopt;
+ }
+ else
+ type = guess_type (u, false); // Can't throw.
+ }
// Call prerequisite repository location constructor, do not amend
// relative path.
@@ -2445,6 +2567,22 @@ namespace bpkg
return r;
}
+ static repository_manifest
+ parse_repository_manifest (parser& p, repository_type base_type, bool iu)
+ {
+ repository_manifest r (
+ parse_repository_manifest (p, p.next (), base_type, 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 repository manifest expected");
+
+ return r;
+ }
+
void repository_manifest::
serialize (serializer& s) const
{
@@ -2519,16 +2657,7 @@ namespace bpkg
repository_manifest
pkg_repository_manifest (parser& p, bool iu)
{
- repository_manifest r (pkg_repository_manifest (p, p.next (), 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 repository manifest expected");
-
- return r;
+ return parse_repository_manifest (p, repository_type::pkg, iu);
}
repository_manifest
@@ -2537,21 +2666,26 @@ namespace bpkg
return parse_repository_manifest (p, nv, repository_type::pkg, iu);
}
- // git_repository_manifest
+ // dir_repository_manifest
//
repository_manifest
- git_repository_manifest (parser& p, bool iu)
+ dir_repository_manifest (parser& p, bool iu)
{
- repository_manifest r (git_repository_manifest (p, p.next (), iu));
+ return parse_repository_manifest (p, repository_type::dir, 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 repository manifest expected");
+ repository_manifest
+ dir_repository_manifest (parser& p, name_value nv, bool iu)
+ {
+ return parse_repository_manifest (p, nv, repository_type::dir, iu);
+ }
- return r;
+ // git_repository_manifest
+ //
+ repository_manifest
+ git_repository_manifest (parser& p, bool iu)
+ {
+ return parse_repository_manifest (p, repository_type::git, iu);
}
repository_manifest
@@ -2560,80 +2694,90 @@ namespace bpkg
return parse_repository_manifest (p, nv, repository_type::git, iu);
}
- // pkg_repository_manifests
+ // Parse the repository manifest list.
//
- pkg_repository_manifests::
- pkg_repository_manifests (parser& p, bool iu)
+ static void
+ parse_repository_manifests (parser& p,
+ repository_type base_type,
+ bool iu,
+ vector<repository_manifest>& ms)
{
name_value nv (p.next ());
while (!nv.empty ())
{
- push_back (pkg_repository_manifest (p, nv, iu));
+ ms.push_back (parse_repository_manifest (p, nv, base_type, iu));
nv = p.next ();
// Make sure there is location in all except the last entry.
//
- if (back ().location.empty () && !nv.empty ())
+ if (ms.back ().location.empty () && !nv.empty ())
throw parsing (p.name (), nv.name_line, nv.name_column,
"repository location expected");
}
- if (empty () || !back ().location.empty ())
+ if (ms.empty () || !ms.back ().location.empty ())
throw parsing (p.name (), nv.name_line, nv.name_column,
"base repository manifest expected");
}
- void pkg_repository_manifests::
- serialize (serializer& s) const
+ // Serialize the repository manifest list.
+ //
+ static void
+ serialize_repository_manifests (serializer& s,
+ const vector<repository_manifest>& ms)
{
- if (empty () || !back ().location.empty ())
+ if (ms.empty () || !ms.back ().location.empty ())
throw serialization (s.name (), "base repository manifest expected");
// @@ Should we check that there is location in all except the last
// entry?
//
- for (const repository_manifest& r: *this)
+ for (const repository_manifest& r: ms)
r.serialize (s);
s.next ("", ""); // End of stream.
}
- // git_repository_manifests
+ // pkg_repository_manifests
//
- git_repository_manifests::
- git_repository_manifests (parser& p, bool iu)
+ pkg_repository_manifests::
+ pkg_repository_manifests (parser& p, bool iu)
{
- name_value nv (p.next ());
- while (!nv.empty ())
- {
- push_back (git_repository_manifest (p, nv, iu));
- nv = p.next ();
+ parse_repository_manifests (p, repository_type::pkg, iu, *this);
+ }
- // 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");
- }
+ void pkg_repository_manifests::
+ serialize (serializer& s) const
+ {
+ serialize_repository_manifests (s, *this);
+ }
- if (empty () || !back ().location.empty ())
- throw parsing (p.name (), nv.name_line, nv.name_column,
- "base repository manifest expected");
+ // dir_repository_manifests
+ //
+ dir_repository_manifests::
+ dir_repository_manifests (parser& p, bool iu)
+ {
+ parse_repository_manifests (p, repository_type::dir, iu, *this);
}
- void git_repository_manifests::
+ void dir_repository_manifests::
serialize (serializer& s) const
{
- if (empty () || !back ().location.empty ())
- throw serialization (s.name (), "base repository manifest expected");
+ serialize_repository_manifests (s, *this);
+ }
- // @@ Should we check that there is location in all except the last
- // entry?
- //
- for (const repository_manifest& r: *this)
- r.serialize (s);
+ // git_repository_manifests
+ //
+ git_repository_manifests::
+ git_repository_manifests (parser& p, bool iu)
+ {
+ parse_repository_manifests (p, repository_type::git, iu, *this);
+ }
- s.next ("", ""); // End of stream.
+ void git_repository_manifests::
+ serialize (serializer& s) const
+ {
+ serialize_repository_manifests (s, *this);
}
// signature_manifest
diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx
index ada6cd3..0e2ee9d 100644
--- a/libbpkg/manifest.hxx
+++ b/libbpkg/manifest.hxx
@@ -395,6 +395,9 @@ namespace bpkg
pkg_package_manifest (butl::manifest_parser&, bool ignore_unknown = false);
LIBBPKG_EXPORT package_manifest
+ dir_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.
@@ -405,6 +408,11 @@ namespace bpkg
bool ignore_unknown = false);
LIBBPKG_EXPORT package_manifest
+ dir_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);
@@ -418,10 +426,13 @@ namespace bpkg
m.serialize (s);
}
- // Normally there is no need to serialize git package manifest, unless for
- // testing.
+ // Normally there is no need to serialize dir and git package manifests,
+ // unless for testing.
//
LIBBPKG_EXPORT void
+ dir_package_manifest (butl::manifest_serializer&, const package_manifest&);
+
+ LIBBPKG_EXPORT void
git_package_manifest (butl::manifest_serializer&, const package_manifest&);
class LIBBPKG_EXPORT pkg_package_manifests:
@@ -445,6 +456,26 @@ namespace bpkg
serialize (butl::manifest_serializer&) const;
};
+ class LIBBPKG_EXPORT dir_package_manifests:
+ public std::vector<package_manifest>
+ {
+ public:
+ using base_type = std::vector<package_manifest>;
+
+ using base_type::base_type;
+
+ public:
+ dir_package_manifests () = default;
+ dir_package_manifests (butl::manifest_parser&,
+ bool ignore_unknown = false);
+
+ // Normally there is no need to serialize dir package manifests, unless for
+ // testing.
+ //
+ void
+ serialize (butl::manifest_serializer&) const;
+ };
+
class LIBBPKG_EXPORT git_package_manifests:
public std::vector<package_manifest>
{
@@ -524,7 +555,7 @@ namespace bpkg
// Repository type.
//
- enum class repository_type {pkg, git};
+ enum class repository_type {pkg, dir, git};
LIBBPKG_EXPORT std::string
to_string (repository_type);
@@ -538,6 +569,15 @@ namespace bpkg
return os << to_string (t);
}
+ // Repository basis.
+ //
+ enum class repository_basis
+ {
+ archive,
+ directory,
+ version_control
+ };
+
// Guess the repository type for the URL:
//
// 1. If scheme is git then git.
@@ -656,6 +696,20 @@ namespace bpkg
return type_;
}
+ repository_basis
+ basis () const
+ {
+ switch (type ())
+ {
+ case repository_type::pkg: return repository_basis::archive;
+ case repository_type::dir: return repository_basis::directory;
+ case repository_type::git: return repository_basis::version_control;
+ }
+
+ assert (false); // Can't be here.
+ return repository_basis::archive;
+ }
+
// URL of an empty location is empty.
//
const repository_url&
@@ -717,20 +771,19 @@ namespace bpkg
bool
archive_based () const
{
- switch (type ())
- {
- case repository_type::pkg: return true;
- case repository_type::git: return false;
- }
+ return basis () == repository_basis::archive;
+ }
- assert (false); // Can't be here.
- return false;
+ bool
+ directory_based () const
+ {
+ return basis () == repository_basis::directory;
}
bool
version_control_based () const
{
- return !archive_based ();
+ return basis () == repository_basis::version_control;
}
// String representation of an empty location is the empty string.
@@ -835,6 +888,10 @@ namespace bpkg
bool ignore_unknown = false);
LIBBPKG_EXPORT repository_manifest
+ dir_repository_manifest (butl::manifest_parser&,
+ bool ignore_unknown = false);
+
+ LIBBPKG_EXPORT repository_manifest
git_repository_manifest (butl::manifest_parser&,
bool ignore_unknown = false);
@@ -846,6 +903,11 @@ namespace bpkg
bool ignore_unknown = false);
LIBBPKG_EXPORT repository_manifest
+ dir_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);
@@ -866,6 +928,22 @@ namespace bpkg
serialize (butl::manifest_serializer&) const;
};
+ class LIBBPKG_EXPORT dir_repository_manifests:
+ public std::vector<repository_manifest>
+ {
+ public:
+ using base_type = std::vector<repository_manifest>;
+
+ using base_type::base_type;
+
+ dir_repository_manifests () = default;
+ dir_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>
{
diff --git a/tests/manifest/driver.cxx b/tests/manifest/driver.cxx
index caca79d..5e028bf 100644
--- a/tests/manifest/driver.cxx
+++ b/tests/manifest/driver.cxx
@@ -16,16 +16,18 @@ using namespace std;
using namespace butl;
using namespace bpkg;
-// Usage: argv[0] (-p|-r|-s)
+// Usage: argv[0] (-pp|-dp|-gp|-pr|-dr|-gr|-s)
//
// Read and parse manifest from STDIN and serialize it to STDOUT. The
// following options specify the manifest type.
//
// -pp parse pkg package manifest list
+// -dp parse dir package manifest list
// -gp parse git package manifest list
// -pr parse pkg repository manifest list
+// -dr parse dir repository manifest list
// -gr parse git repository manifest list
-// -s parse signature manifest
+// -s parse signature manifest
//
int
main (int argc, char* argv[])
@@ -41,10 +43,14 @@ main (int argc, char* argv[])
if (opt == "-pp")
pkg_package_manifests (p).serialize (s);
- else if (opt == "-pr")
- pkg_repository_manifests (p).serialize (s);
+ else if (opt == "-dp")
+ dir_package_manifests (p).serialize (s);
else if (opt == "-gp")
git_package_manifests (p).serialize (s);
+ else if (opt == "-pr")
+ pkg_repository_manifests (p).serialize (s);
+ else if (opt == "-dr")
+ dir_repository_manifests (p).serialize (s);
else if (opt == "-gr")
git_repository_manifests (p).serialize (s);
else if (opt == "-s")
diff --git a/tests/manifest/testscript b/tests/manifest/testscript
index 68ceced..b72f492 100644
--- a/tests/manifest/testscript
+++ b/tests/manifest/testscript
@@ -260,6 +260,37 @@
url: http://cppget.org
EOO
}
+
+ : dir
+ :
+ {
+ : manifest
+ :
+ : Roundtrip the dir repository manifest list.
+ :
+ $* -dr <<EOF >>EOF
+ : 1
+ location: ../stable
+ type: dir
+ role: complement
+ :
+ EOF
+
+ : prerequisite-with-fragment
+ :
+ $* -dr <<EOI >>EOO
+ : 1
+ location: ../stable.git#stable
+ role: complement
+ :
+ EOI
+ : 1
+ location: ../stable.git
+ type: dir
+ role: complement
+ :
+ EOO
+ }
}
: signature
diff --git a/tests/repository-location/driver.cxx b/tests/repository-location/driver.cxx
index 89a32f8..014b810 100644
--- a/tests/repository-location/driver.cxx
+++ b/tests/repository-location/driver.cxx
@@ -201,6 +201,10 @@ namespace bpkg
assert (bad_loc ("file:/abc", repository_type::git));
#endif
+ // Can't be remote.
+ //
+ assert (bad_loc ("http://example.com/dir", repository_type::dir));
+
// Invalid web interface URL.
//
assert (bad_url (".a/..", loc ("http://stable.cppget.org/1/misc")));
@@ -321,6 +325,11 @@ namespace bpkg
assert (l.string () == "file:/#master");
assert (l.canonical_name () == "git:/#master");
}
+ {
+ repository_location l (loc ("/home/user/repo", repository_type::dir));
+ assert (l.string () == "/home/user/repo");
+ assert (l.canonical_name () == "dir:/home/user/repo");
+ }
#else
{
repository_location l (loc ("c:\\1\\aa\\bb", loc ()));
@@ -388,6 +397,11 @@ namespace bpkg
assert (l.string () == "file:/c:#master");
assert (l.canonical_name () == "git:c:#master");
}
+ {
+ repository_location l (loc ("c:\\user\\repo", repository_type::dir));
+ assert (l.string () == "c:\\user\\repo");
+ assert (l.canonical_name () == "dir:c:\\user\\repo");
+ }
#endif
{
repository_location l (loc ("../c/../c/./1/aa/../bb", loc ()));