aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libbutl/standard-version.cxx199
-rw-r--r--libbutl/standard-version.mxx8
-rw-r--r--tests/standard-version/driver.cxx23
-rw-r--r--tests/standard-version/testscript137
4 files changed, 327 insertions, 40 deletions
diff --git a/libbutl/standard-version.cxx b/libbutl/standard-version.cxx
index 74991ad..41b3611 100644
--- a/libbutl/standard-version.cxx
+++ b/libbutl/standard-version.cxx
@@ -591,12 +591,52 @@ namespace butl
standard_version::allow_earliest);
}
- standard_version_constraint::
- standard_version_constraint (const std::string& s)
+ // Parse the version constraint, optionally completing it using the
+ // specified dependent package version.
+ //
+ static standard_version_constraint
+ parse_constraint (const string& s, const standard_version* v)
{
using std::string; // Not to confuse with string().
auto bail = [] (const string& m) {throw invalid_argument (m);};
+
+ // The dependent package version can't be empty or earliest. It, however,
+ // can be a stub (think of build-time dependencies).
+ //
+ if (v != nullptr)
+ {
+ if (v->empty ())
+ bail ("dependent version is empty");
+
+ if (v->earliest ())
+ bail ("dependent version is earliest");
+ }
+
+ // Strip the dependent version revision. Fail for stubs and latest
+ // snapshots, which are meaningless to refer to from the constraint.
+ // Cache the result on the first call.
+ //
+ standard_version dv;
+ auto dependent_version = [v, &dv, &bail] () -> const standard_version&
+ {
+ if (dv.empty ())
+ {
+ assert (v != nullptr);
+
+ if (v->latest_snapshot ())
+ bail ("dependent version is latest snapshot");
+
+ if (v->stub ())
+ bail ("dependent version is stub");
+
+ dv = *v;
+ dv.revision = 0;
+ }
+
+ return dv;
+ };
+
const char* spaces (" \t");
size_t p (0);
@@ -616,8 +656,11 @@ namespace butl
try
{
- min_version = standard_version (s.substr (p, e - p),
- standard_version::allow_earliest);
+ string mnv (s, p, e - p);
+
+ min_version = v != nullptr && mnv == "$"
+ ? dependent_version ()
+ : standard_version (mnv, standard_version::allow_earliest);
}
catch (const invalid_argument& e)
{
@@ -634,8 +677,11 @@ namespace butl
try
{
- max_version = standard_version (s.substr (p, e - p),
- standard_version::allow_earliest);
+ string mxv (s, p, e - p);
+
+ max_version = v != nullptr && mxv == "$"
+ ? dependent_version ()
+ : standard_version (mxv, standard_version::allow_earliest);
}
catch (const invalid_argument& e)
{
@@ -651,10 +697,10 @@ namespace butl
if (++p != s.size ())
bail ("junk after constraint");
- // Verify and copy the constraint.
+ // Can throw.
//
- *this = standard_version_constraint (move (min_version), min_open,
- move (max_version), max_open);
+ return standard_version_constraint (move (min_version), min_open,
+ move (max_version), max_open);
}
else if (c == '~' || c == '^')
{
@@ -662,19 +708,95 @@ namespace butl
if (p == string::npos)
bail ("no version");
- // Can throw.
- //
- standard_version min_version (
- standard_version (s.substr (p), standard_version::allow_earliest));
+ standard_version min_version;
+ standard_version max_version;
- // Can throw.
- //
- standard_version max_version (
- shortcut_max_version (c, min_version, false));
+ try
+ {
+ string cv (s, p);
+ if (v != nullptr && cv == "$") // Dependent version reference.
+ {
+ const standard_version& dv (dependent_version ());
+
+ // For a release set the min version endpoint patch to zero. For ^
+ // also set the minor version to zero, unless the major version is
+ // zero (reduced to ~).
+ //
+ if (dv.release ())
+ {
+ min_version = standard_version (
+ dv.epoch,
+ dv.major (),
+ c == '^' && dv.major () != 0 ? 0 : dv.minor (),
+ 0 /* patch */);
+ }
+ //
+ // For a final pre-release or a patch snapshot we check if there has
+ // been a compatible final release (patch is not zero for ~ and
+ // minor/patch are not zero for ^). If that's the case, then
+ // fallback to the release case and start the range from the first
+ // alpha otherwise.
+ //
+ else if (dv.final () || (dv.snapshot () && dv.patch () != 0))
+ {
+ min_version = standard_version (
+ dv.epoch,
+ dv.major (),
+ c == '^' && dv.major () != 0 ? 0 : dv.minor (),
+ 0 /* patch */,
+ dv.patch () != 0 || (c == '^' && dv.minor () != 0)
+ ? 0
+ : 1 /* pre-release */);
+ }
+ //
+ // For a major/minor snapshot we assume that all the packages are
+ // developed in the lockstep and convert the constraint range to
+ // represent this "snapshot series".
+ //
+ else
+ {
+ assert (dv.snapshot () && dv.patch () == 0);
+
+ uint16_t pr (*dv.pre_release ());
+
+ min_version = standard_version (dv.epoch,
+ dv.major (),
+ dv.minor (),
+ 0 /* patch */,
+ pr,
+ 1 /* snapshot_sn */,
+ "" /* snapshot_id */);
+
+ max_version = standard_version (dv.epoch,
+ dv.major (),
+ dv.minor (),
+ 0 /* patch */,
+ pr + 1);
+ }
+ }
+ else // Version is specified literally.
+ {
+ // Can throw.
+ //
+ min_version = standard_version (cv,
+ standard_version::allow_earliest);
+ }
+
+ // If the max version is not set for the lockstep (see above), then we
+ // set it normally.
+ //
+ if (max_version.empty ())
+ max_version = shortcut_max_version (
+ c, min_version, false /* ignore_overflow */); // Can throw.
+ }
+ catch (const invalid_argument& e)
+ {
+ bail (string ("invalid version: ") + e.what ());
+ }
try
{
- *this = standard_version_constraint (
+ return standard_version_constraint (
move (min_version), false /* min_open */,
move (max_version), true /* max_open */);
}
@@ -709,11 +831,15 @@ namespace butl
if (p == string::npos)
bail ("no version");
- standard_version v;
+ standard_version cv;
try
{
- v = standard_version (s.substr (p),
+ string cver (s, p);
+
+ cv = v != nullptr && cver == "$"
+ ? dependent_version ()
+ : standard_version (cver,
operation != comparison::eq
? standard_version::allow_earliest
: standard_version::none);
@@ -728,22 +854,35 @@ namespace butl
switch (operation)
{
case comparison::eq:
- *this = standard_version_constraint (v);
- break;
+ return standard_version_constraint (cv);
case comparison::lt:
- *this = standard_version_constraint (nullopt, true, move (v), true);
- break;
+ return standard_version_constraint (nullopt, true, move (cv), true);
case comparison::le:
- *this = standard_version_constraint (nullopt, true, move (v), false);
- break;
+ return standard_version_constraint (nullopt, true, move (cv), false);
case comparison::gt:
- *this = standard_version_constraint (move (v), true, nullopt, true);
- break;
+ return standard_version_constraint (move (cv), true, nullopt, true);
case comparison::ge:
- *this = standard_version_constraint (move (v), false, nullopt, true);
- break;
+ return standard_version_constraint (move (cv), false, nullopt, true);
}
}
+
+ // Can't be here.
+ //
+ assert (false);
+ return standard_version_constraint ();
+ }
+
+ standard_version_constraint::
+ standard_version_constraint (const std::string& s)
+ {
+ *this = parse_constraint (s, nullptr /* dependent_version */);
+ }
+
+ standard_version_constraint::
+ standard_version_constraint (const std::string& s,
+ const standard_version& dependent_version)
+ {
+ *this = parse_constraint (s, &dependent_version);
}
standard_version_constraint::
diff --git a/libbutl/standard-version.mxx b/libbutl/standard-version.mxx
index 6688237..d9e46c7 100644
--- a/libbutl/standard-version.mxx
+++ b/libbutl/standard-version.mxx
@@ -287,6 +287,8 @@ LIBBUTL_MODEXPORT namespace butl
// ('^' | '~') <version>
// ('(' | '[') <version> <version> (')' | ']')
//
+ // The version may be `$` which refers to the dependent package version.
+ //
struct LIBBUTL_SYMEXPORT standard_version_constraint
{
butl::optional<standard_version> min_version;
@@ -299,6 +301,12 @@ LIBBUTL_MODEXPORT namespace butl
explicit
standard_version_constraint (const std::string&);
+ // As above but also completes the special `$` version using the specified
+ // dependent package version.
+ //
+ standard_version_constraint (const std::string&,
+ const standard_version& dependent_version);
+
// Throw std::invalid_argument if the specified version range is invalid.
//
standard_version_constraint (
diff --git a/tests/standard-version/driver.cxx b/tests/standard-version/driver.cxx
index 353cc24..72dbb4a 100644
--- a/tests/standard-version/driver.cxx
+++ b/tests/standard-version/driver.cxx
@@ -147,7 +147,7 @@ version (const string& s,
//
// argv[0] (-rl|-pr|-al|-bt|-st|-el|-sn|-fn) <version>
// argv[0] -cm <version> <version>
-// argv[0] -cr
+// argv[0] -cr [<dependent-version>]
// argv[0] -sf <version> <constraint>
// argv[0]
//
@@ -161,7 +161,8 @@ version (const string& s,
// -fn output 'y' for final, 'n' otherwise
//
// -cm output 0 if versions are equal, -1 if the first one is less, 1 otherwise
-// -cr create version constraints from stdin lines, and print them to stdout
+// -cr create version constraints from stdin lines, optionally using the
+// dependent version, and print them to stdout
// -sf output 'y' if version satisfies constraint, 'n' otherwise
//
// If no options are specified, then create versions from stdin lines, and
@@ -241,11 +242,25 @@ try
}
else if (o == "-cr")
{
- assert (argc == 2);
+ assert (argc <= 3);
+
+ optional<standard_version> dv;
+ if (argc == 3)
+ {
+ string s (argv[2]);
+
+ dv = s.empty ()
+ ? standard_version ()
+ : standard_version (s,
+ standard_version::allow_stub |
+ standard_version::allow_earliest);
+ }
string s;
while (getline (cin, s))
- cout << standard_version_constraint (s) << endl;
+ cout << (dv
+ ? standard_version_constraint (s, *dv)
+ : standard_version_constraint (s)) << endl;
}
else if (o == "-sf")
{
diff --git a/tests/standard-version/testscript b/tests/standard-version/testscript
index cfe3b90..8a520a7 100644
--- a/tests/standard-version/testscript
+++ b/tests/standard-version/testscript
@@ -456,16 +456,141 @@
: invalid
:
{
- $* <'-1.2.3' 2>'invalid constraint' == 1 : bad-char
- $* <'~' 2>'no version' == 1 : no-version
- $* <'~1.2' 2>"'.' expected after minor version" == 1 : bad-ver
- $* <'~1.999.0' 2>"invalid minor version" == 1 : bad-min-tilde
- $* <'^0.999.0' 2>"invalid minor version" == 1 : bad-min-caret
- $* <'^999.0.0' 2>"invalid major version" == 1 : bad-maj-caret
+ $* <'-1.2.3' 2>'invalid constraint' == 1 : bad-char
+ $* <'~' 2>'no version' == 1 : no-version
+ $* <'~1.2' 2>"invalid version: '.' expected after minor version" == 1 : bad-ver
+ $* <'~1.999.0' 2>"invalid version: invalid minor version" == 1 : bad-min-tilde
+ $* <'^0.999.0' 2>"invalid version: invalid minor version" == 1 : bad-min-caret
+ $* <'^999.0.0' 2>"invalid version: invalid major version" == 1 : bad-maj-caret
}
}
}
+: constraints-dependent
+:
+{
+ test.options += -cr
+
+ : range
+ :
+ {
+ : valid
+ :
+ $* '1.2.3+1' <<EOI >>EOE
+ [1.2.2 $]
+ (1.2.2 $)
+ [$ 1.2.4]
+ ($ 1.2.4]
+ EOI
+ [1.2.2 1.2.3]
+ (1.2.2 1.2.3)
+ [1.2.3 1.2.4]
+ (1.2.3 1.2.4]
+ EOE
+ }
+
+ : comparison
+ :
+ {
+ : valid
+ :
+ $* '1.2.3+1' <<EOI >>EOE
+ == $
+ >= $
+ <= $
+ > $
+ < $
+ >= $
+ <= $
+ > $
+ < $
+ EOI
+ == 1.2.3
+ >= 1.2.3
+ <= 1.2.3
+ > 1.2.3
+ < 1.2.3
+ >= 1.2.3
+ <= 1.2.3
+ > 1.2.3
+ < 1.2.3
+ EOE
+
+ : invalid
+ :
+ {
+ $* '' <'== $' 2>'dependent version is empty' == 1 : empty-version
+ $* '1.2.3-' <'== $' 2>'dependent version is earliest' == 1 : earliest-version
+ $* '1.2.3-a.0.z' <'== $' 2>'invalid version: dependent version is latest snapshot' == 1 : latest-version
+ $* '0+1' <'== $' 2>'invalid version: dependent version is stub' == 1 : stub-version
+ }
+ }
+
+ : shortcut
+ :
+ {
+ : final
+ :
+ {
+ $* '1.2.3+1' <<EOI >>EOE
+ ~$
+ ^$
+ EOI
+ ~1.2.0
+ ^1.0.0
+ EOE
+ }
+
+ : pre-release
+ :
+ {
+ : tilda
+ :
+ {
+ $* '1.2.0-b.2' <'~$' >'~1.2.0-a.1' : no-final
+ $* '1.2.1-a.1' <'~$' >'~1.2.0' : final-patch
+ }
+
+ : carrot
+ :
+ {
+ $* '1.0.0-b.2' <'^$' >'^1.0.0-a.1' : no-final
+ $* '1.0.1-a.1' <'^$' >'^1.0.0' : final-patch
+ $* '1.1.0-b.2' <'^$' >'^1.0.0' : final-minor
+ }
+ }
+
+ : snapshot
+ :
+ {
+ : tilda
+ :
+ {
+ $* '1.2.1-a.2.345' <'~$' >'~1.2.0' : patch
+ $* '1.2.0-a.0.345' <'~$' >'[1.2.0-a.0.1 1.2.0-a.1)' : minor
+ $* '1.0.0-a.0.345' <'~$' >'[1.0.0-a.0.1 1.0.0-a.1)' : major
+ }
+
+ : carrot
+ :
+ {
+ $* '1.2.1-a.2.345' <'^$' >'^1.0.0' : patch
+ $* '1.2.0-a.0.345' <'^$' >'[1.2.0-a.0.1 1.2.0-a.1)' : minor
+ $* '1.0.0-a.0.345' <'^$' >'[1.0.0-a.0.1 1.0.0-a.1)' : major
+ }
+ }
+ }
+
+ : invalid
+ :
+ {
+ $* <'[1.2.2 $]' 2>'invalid max version: invalid major version' != 0 : max
+ $* <'[$ 1.2.2]' 2>'invalid min version: invalid major version' != 0 : min
+ $* <'== $' 2>'invalid version: invalid major version' != 0 : eq
+ $* <'~$' 2>'invalid version: invalid major version' != 0 : shortcut
+ }
+}
+
: satisfaction
:
{