aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bpkg/manifest43
-rw-r--r--bpkg/manifest.cxx66
-rw-r--r--tests/repository-location/driver.cxx101
3 files changed, 196 insertions, 14 deletions
diff --git a/bpkg/manifest b/bpkg/manifest
index e8c2714..0ec3a3a 100644
--- a/bpkg/manifest
+++ b/bpkg/manifest
@@ -302,14 +302,32 @@ namespace bpkg
//
repository_location () = default;
+ // Creates remote/absolute repository location. Throws invalid_argument
+ // if the location is a relative path.
+ //
explicit
repository_location (const std::string&);
+ // Creates a potentially relative repository location. If base is not
+ // empty, use it to complete the relative location to remote/absolute.
+ // Throws invalid_argument if base itself is relative or the resulting
+ // completed location is invalid.
+ //
+ repository_location (const std::string&, const repository_location& base);
+
+ // Note that relative locations have no canonical name.
+ //
const std::string&
canonical_name () const noexcept {return canonical_name_;}
+ // There are 3 types of locations: remote, local absolute filesystem
+ // path and local relative filesystem path. Plus there is the special
+ // empty location. The following predicates can be used to determine
+ // what kind of location it is. Note that except for empty(), all the
+ // other predicates throw std::logic_error for an empty location.
+ //
bool
- empty () const noexcept {return canonical_name_.empty ();}
+ empty () const noexcept {return path_.empty ();}
bool
local () const
@@ -320,6 +338,29 @@ namespace bpkg
return host_.empty ();
}
+ bool
+ remote () const
+ {
+ return !local ();
+ }
+
+ bool
+ absolute () const
+ {
+ return local () && path_.absolute ();
+ }
+
+ bool
+ relative () const
+ {
+ if (empty ())
+ throw std::logic_error ("empty location");
+
+ // Note that in remote locations path is always absolute.
+ //
+ return path_.relative ();
+ }
+
const butl::dir_path&
path () const
{
diff --git a/bpkg/manifest.cxx b/bpkg/manifest.cxx
index 0393f6f..5e8da1a 100644
--- a/bpkg/manifest.cxx
+++ b/bpkg/manifest.cxx
@@ -748,14 +748,29 @@ namespace bpkg
// repository_location
//
+ // Location parameter type is fully qualified as compiler gets confused with
+ // string() member.
+ //
+ repository_location::
+ repository_location (const std::string& l)
+ : repository_location (l, repository_location ()) // Delegate.
+ {
+ if (relative ())
+ throw invalid_argument ("relative filesystem path");
+ }
+
repository_location::
- repository_location (const std::string& l): port_ (0)
+ repository_location (const std::string& l, const repository_location& b)
{
- // Otherwise compiler gets confused with string() member. Same reason
- // constructor parameter type is fully qualified.
+ // Otherwise compiler gets confused with string() member.
//
using std::string;
+ // Base repository location can not be a relative path.
+ //
+ if (!b.empty () && b.relative ())
+ throw invalid_argument ("base relative filesystem path");
+
if (::strncasecmp (l.c_str (), "http://", 7) == 0)
{
// Split location into host, port and path components. Calculate
@@ -842,7 +857,9 @@ namespace bpkg
// Chop the port, if present.
//
- if (pt != he)
+ if (pt == he)
+ port_ = 0;
+ else
{
unsigned long long n (++pt == he ? 0 : stoull (string (pt, he)));
if (n == 0 || n > UINT16_MAX)
@@ -874,8 +891,31 @@ namespace bpkg
canonical_name_ += ':' + to_string (port_);
}
else
+ {
path_ = dir_path (l);
+ if (path_.empty ())
+ throw invalid_argument ("empty location");
+
+ // Complete if we are relative and have base.
+ //
+ if (!b.empty () && path_.relative ())
+ {
+ // Convert the relative path location to an absolute or remote one.
+ //
+ host_ = b.host_;
+ port_ = b.port_;
+ path_ = b.path_ / path_;
+
+ // Set canonical name to the base location canonical name host
+ // part. The path part of the canonical name is calculated below.
+ //
+ if (b.remote ())
+ canonical_name_ =
+ b.canonical_name_.substr (0, b.canonical_name_.find ("/"));
+ }
+ }
+
// Normalize path to avoid different representations of the same location
// and canonical name. So a/b/../c/1/x/../y and a/c/1/y to be considered
// as same location.
@@ -889,14 +929,19 @@ namespace bpkg
throw invalid_argument ("invalid path");
}
+ // Finish calculating the canonical name, unless we are relative.
+ //
+ if (relative ())
+ return;
+
// Search for the version path component preceeding canonical name
// <path> component.
//
- auto b (path_.rbegin ()), i (b), e (path_.rend ());
+ auto rb (path_.rbegin ()), i (rb), re (path_.rend ());
// Find the version component.
//
- for (; i != e; ++i)
+ for (; i != re; ++i)
{
const string& c (*i);
@@ -904,7 +949,7 @@ namespace bpkg
break;
}
- if (i == e)
+ if (i == re)
throw invalid_argument ("missing repository version");
// Validate the version. At the moment the only valid value is 1.
@@ -914,7 +959,7 @@ namespace bpkg
// Note: allow empty paths (e.g., http://stable.cppget.org/1/).
//
- string d (dir_path (b, i).posix_string ());
+ string d (dir_path (rb, i).posix_string ());
if (!canonical_name_.empty () && !d.empty ()) // If we have host and dir.
canonical_name_ += '/';
@@ -986,7 +1031,10 @@ namespace bpkg
{
try
{
- location = repository_location (move (v));
+ // Call prerequisite repository location constructor, do not
+ // ammend relative path.
+ //
+ location = repository_location (move (v), repository_location ());
}
catch (const invalid_argument& e)
{
diff --git a/tests/repository-location/driver.cxx b/tests/repository-location/driver.cxx
index be51eb0..4eab115 100644
--- a/tests/repository-location/driver.cxx
+++ b/tests/repository-location/driver.cxx
@@ -27,6 +27,20 @@ bad_location (const string& l)
}
}
+static bool
+bad_location (const string& l, const repository_location& b)
+{
+ try
+ {
+ repository_location bl (l, b);
+ return false;
+ }
+ catch (const invalid_argument)
+ {
+ return true;
+ }
+}
+
int
main (int argc, char* argv[])
{
@@ -88,22 +102,40 @@ main (int argc, char* argv[])
//
assert (bad_location ("3/aaa/bbb"));
+ // Invalid prerequisite repository location.
+ //
+ assert (bad_location ("a/c/1/bb"));
+
+ assert (bad_location ("a/c/1/bb",
+ repository_location ("./var/1/stable",
+ repository_location ())));
+
+ assert (bad_location ("../../../1/math",
+ repository_location (
+ "http://stable.cppget.org/1/misc")));
+
+
// Test valid locations.
//
{
- repository_location l ("1/aa/bb");
+ repository_location l ("1/aa/bb", repository_location ());
assert (l.string () == "1/aa/bb");
+ assert (l.canonical_name ().empty ());
+ }
+ {
+ repository_location l ("/1/aa/bb", repository_location ());
+ assert (l.string () == "/1/aa/bb");
assert (l.canonical_name () == "aa/bb");
}
{
- repository_location l ("/a/b/../c/1/aa/../bb");
+ repository_location l ("/a/b/../c/1/aa/../bb", repository_location ());
assert (l.string () == "/a/c/1/bb");
assert (l.canonical_name () == "bb");
}
{
- repository_location l ("../c/../c/./1/aa/../bb");
+ repository_location l ("../c/../c/./1/aa/../bb", repository_location ());
assert (l.string () == "../c/1/bb");
- assert (l.canonical_name () == "bb");
+ assert (l.canonical_name ().empty ());
}
{
repository_location l ("http://www.a.com:80/1/aa/bb");
@@ -144,6 +176,67 @@ main (int argc, char* argv[])
repository_location l ("http://stable.cppget.org/1/");
assert (l.canonical_name () == "stable.cppget.org");
}
+ {
+ repository_location l1 ("http://stable.cppget.org/1/misc");
+ repository_location l2 ("../../1/math", l1);
+ assert (l2.string () == "http://stable.cppget.org/1/math");
+ assert (l2.canonical_name () == "stable.cppget.org/math");
+ }
+ {
+ repository_location l1 ("http://stable.cppget.org/1/misc");
+ repository_location l2 ("../math", l1);
+ assert (l2.string () == "http://stable.cppget.org/1/math");
+ assert (l2.canonical_name () == "stable.cppget.org/math");
+ }
+ {
+ repository_location l1 ("http://www.stable.cppget.org:8080/1");
+ repository_location l2 ("../1/math", l1);
+ assert (l2.string () == "http://www.stable.cppget.org:8080/1/math");
+ assert (l2.canonical_name () == "stable.cppget.org:8080/math");
+ }
+ {
+ repository_location l1 ("/var/r1/1/misc");
+ repository_location l2 ("../../../r2/1/math", l1);
+ assert (l2.string () == "/var/r2/1/math");
+ assert (l2.canonical_name () == "math");
+ }
+ {
+ repository_location l1 ("/var/1/misc");
+ repository_location l2 ("../math", l1);
+ assert (l2.string () == "/var/1/math");
+ assert (l2.canonical_name () == "math");
+ }
+ {
+ repository_location l1 ("/var/1/stable");
+ repository_location l2 ("/var/1/test", l1);
+ assert (l2.string () == "/var/1/test");
+ assert (l2.canonical_name () == "test");
+ }
+ {
+ repository_location l1 ("http://stable.cppget.org/1/misc");
+ repository_location l2 ("/var/1/test", l1);
+ assert (l2.string () == "/var/1/test");
+ assert (l2.canonical_name () == "test");
+ }
+ {
+ repository_location l1 ("http://www.cppget.org/1/stable");
+ repository_location l2 ("http://abc.com/1/test", l1);
+ assert (l2.string () == "http://abc.com/1/test");
+ assert (l2.canonical_name () == "abc.com/test");
+ }
+ {
+ repository_location l1 ("http://stable.cppget.org/1/");
+ repository_location l2 ("http://stable.cppget.org/1/",
+ repository_location ());
+ assert (l1.string () == l2.string ());
+ assert (l1.canonical_name () == l2.canonical_name ());
+ }
+ {
+ repository_location l1 ("/var/1/stable");
+ repository_location l2 ("/var/1/stable", repository_location ());
+ assert (l1.string () == l2.string ());
+ assert (l1.canonical_name () == l2.canonical_name ());
+ }
}
catch (const exception& e)
{