From 332c4e09b11b010a6ad50468230758d0874dbd60 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sat, 29 Apr 2017 08:01:41 +0200 Subject: Add support for stub standard version --- butl/standard-version | 41 ++++++++-- butl/standard-version.cxx | 161 +++++++++++++++++++++++--------------- butl/standard-version.ixx | 32 +++++++- tests/standard-version/driver.cxx | 14 ++-- tests/standard-version/testscript | 24 +++++- 5 files changed, 190 insertions(+), 82 deletions(-) diff --git a/butl/standard-version b/butl/standard-version index c198d3e..ae26d71 100644 --- a/butl/standard-version +++ b/butl/standard-version @@ -16,10 +16,11 @@ namespace butl { - // The build2 "standard version" (specific and earliest): + // The build2 "standard version" (specific, earliest and stub): // // [~]..[-(a|b).[.[.]]][+] // [~]..- + // 0[+] // struct LIBBUTL_EXPORT standard_version { @@ -29,7 +30,9 @@ namespace butl // ? (E == 1) || (snapshot_sn == 0) // : (E == 0) == (snapshot_sn == 0) // - // 2. snapshot_sn != latest_sn && snapshot_sn != 0 || snapshot_id.empty () + // 2. version != 0 || allow_stub && epoch == 0 && snapshot_sn == 0 + // + // 3. snapshot_sn != latest_sn && snapshot_sn != 0 || snapshot_id.empty () // static const std::uint64_t latest_sn = std::uint64_t (~0); @@ -62,13 +65,16 @@ namespace butl bool beta () const noexcept; bool snapshot () const noexcept {return snapshot_sn != 0;} - // Is represented by DDDE being 0001 and snapshot_sn being 0. + // Represented by DDDE in version being 0001 and snapshot_sn being 0. // // Note that the earliest version is a final alpha pre-release. // bool earliest () const noexcept; + bool + stub () const noexcept {return version == 0;} + int compare (const standard_version& v) const noexcept { @@ -90,28 +96,35 @@ namespace butl // Parse the version. Throw std::invalid_argument if the format is not // recognizable or components are invalid. // + enum flags + { + none = 0, + allow_earliest = 0x01, // Allow ..- form. + allow_stub = 0x02 // Allow 0[+] form. + }; + explicit - standard_version (const std::string&, bool allow_earliest = false); + standard_version (const std::string&, flags = none); explicit - standard_version (std::uint64_t version, bool allow_earliest = false); + standard_version (std::uint64_t version, flags = none); standard_version (std::uint64_t version, const std::string& snapshot, - bool allow_earliest = false); + flags = none); standard_version (std::uint16_t epoch, std::uint64_t version, const std::string& snapshot, std::uint16_t revision, - bool allow_earliest = false); + flags = none); standard_version (std::uint16_t epoch, std::uint64_t version, std::uint64_t snapshot_sn, std::string snapshot_id, std::uint16_t revision, - bool allow_earliest = false); + flags = none); // Create empty version. // @@ -164,6 +177,18 @@ namespace butl return o << x.string (); } + inline standard_version::flags + operator& (standard_version::flags, standard_version::flags); + + inline standard_version::flags + operator| (standard_version::flags, standard_version::flags); + + inline standard_version::flags + operator&= (standard_version::flags&, standard_version::flags); + + inline standard_version::flags + operator|= (standard_version::flags&, standard_version::flags); + // The build2 "standard version" constraint: // // ('==' | '>' | '<' | '>=' | '<=') diff --git a/butl/standard-version.cxx b/butl/standard-version.cxx index 1952653..74a5848 100644 --- a/butl/standard-version.cxx +++ b/butl/standard-version.cxx @@ -38,32 +38,32 @@ namespace butl } static void - check_version (uint64_t version, bool snapshot, bool allow_earliest) + check_version (uint64_t vr, bool sn, standard_version::flags fl) { // Check that the version isn't too large. // - // AAABBBCCCDDDE - bool r (version < 10000000000000ULL); + // AAABBBCCCDDDE + bool r (vr < 10000000000000ULL); // Check that E version component is consistent with the snapshot flag. - // Note that if the allow_earliest flag is true, then E can be 1 for the + // Note that if the allow_earliest flag is set, then E can be 1 for the // snapshot flag being false, denoting the earliest pre-release of the // version. // if (r) { - uint64_t e (version % 10); - if (!allow_earliest) - r = e == (snapshot ? 1 : 0); + uint64_t e (vr % 10); + if ((fl & standard_version::allow_earliest) == 0) + r = e == (sn ? 1 : 0); else - r = e == 1 || (e == 0 && !snapshot); + r = e == 1 || (e == 0 && !sn); } // Check that pre-release number is consistent with the snapshot flag. // if (r) { - uint64_t ab (version / 10 % 1000); + uint64_t ab (vr / 10 % 1000); // Note that if ab is 0, it can either mean non-pre-release version in // the absence of snapshot number, or 'a.0' pre-release otherwise. If ab @@ -71,14 +71,16 @@ namespace butl // number. // if (ab != 0) - r = ab != 500 || snapshot; + r = ab != 500 || sn; } - // Check that the major, the minor and the bugfix versions are not - // simultaneously zeros. + // Check that the major, the minor and the patch versions are not + // simultaneously zeros, unless stub is allowed, in which case the snapshot + // flag must be false. // if (r) - r = (version / 10000) != 0; + r = (vr / 10000) != 0 || + ((fl & standard_version::allow_stub) != 0 && !sn); if (!r) throw invalid_argument ("invalid project version"); @@ -87,7 +89,7 @@ namespace butl // standard_version // standard_version:: - standard_version (const std::string& s, bool allow_earliest) + standard_version (const std::string& s, flags f) { auto bail = [] (const char* m) {throw invalid_argument (m);}; @@ -101,7 +103,10 @@ namespace butl ep = *e == '~'; } - size_t p (0); + // Note that here and below p is less or equal n, and so s[p] is always + // valid. + // + size_t p (0), n (s.size ()); if (ep) { @@ -110,59 +115,70 @@ namespace butl } uint16_t ma, mi, bf, ab (0); + bool earliest (false); ma = parse_num (s, p, "invalid major version"); - // Note that here and below p is less or equal n, and so s[p] is always - // valid. + // The only valid version that has no epoch, contains only the major + // version being equal to zero, that is optionally followed by the plus + // character, is the stub version, unless forbidden. For such a version + // we go straight to the package revision parsing. // - if (s[p] != '.') - bail ("'.' expected after major version"); + bool stub ((f & allow_stub) != 0 && !ep && ma == 0 && + (p == n || s[p] == '+')); - mi = parse_num (s, ++p, "invalid minor version"); + if (!stub) + { + if (s[p] != '.') + bail ("'.' expected after major version"); - if (s[p] != '.') - bail ("'.' expected after minor version"); + mi = parse_num (s, ++p, "invalid minor version"); - bf = parse_num (s, ++p, "invalid bugfix version"); + if (s[p] != '.') + bail ("'.' expected after minor version"); - // AAABBBCCCDDDE - version = ma * 10000000000ULL + - mi * 10000000ULL + - bf * 10000ULL; + bf = parse_num (s, ++p, "invalid patch version"); - if (version == 0) - bail ("0.0.0 version"); + // AAABBBCCCDDDE + version = ma * 10000000000ULL + + mi * 10000000ULL + + bf * 10000ULL; - // Parse the pre-release component if present. - // - bool earliest (false); - if (s[p] == '-') - { - char k (s[++p]); + if (version == 0) + bail ("0.0.0 version"); - if (k == '\0' && allow_earliest) // Dash is the last string character. - earliest = true; - else + // Parse the pre-release component if present. + // + if (s[p] == '-') { - if (k != 'a' && k != 'b') - bail ("'a' or 'b' expected in pre-release"); - - if (s[++p] != '.') - bail ("'.' expected after pre-release letter"); - - ab = parse_num (s, ++p, "invalid pre-release", 0, 499); - - if (k == 'b') - ab += 500; + char k (s[++p]); - // Parse the snapshot components if present. Note that pre-release number - // can't be zero for the final pre-release. + // If the last character in the string is dash, then this is the + // earliest version pre-release, unless forbidden. // - if (s[p] == '.') - parse_snapshot (s, ++p); - else if (ab == 0 || ab == 500) - bail ("invalid final pre-release"); + if (k == '\0' && (f & allow_earliest) != 0) + earliest = true; + else + { + if (k != 'a' && k != 'b') + bail ("'a' or 'b' expected in pre-release"); + + if (s[++p] != '.') + bail ("'.' expected after pre-release letter"); + + ab = parse_num (s, ++p, "invalid pre-release", 0, 499); + + if (k == 'b') + ab += 500; + + // Parse the snapshot components if present. Note that pre-release + // number can't be zero for the final pre-release. + // + if (s[p] == '.') + parse_snapshot (s, ++p); + else if (ab == 0 || ab == 500) + bail ("invalid final pre-release"); + } } } @@ -173,7 +189,7 @@ namespace butl revision = parse_num (s, ++p, "invalid revision", 1, uint16_t (~0)); } - if (p != s.size ()) + if (p != n) bail ("junk after version"); if (ab != 0 || snapshot_sn != 0 || earliest) @@ -184,18 +200,18 @@ namespace butl } standard_version:: - standard_version (uint64_t v, bool allow_earliest) + standard_version (uint64_t v, flags f) : version (v) { - check_version (v, false, allow_earliest); + check_version (v, false, f); } standard_version:: - standard_version (uint64_t v, const std::string& s, bool allow_earliest) + standard_version (uint64_t v, const std::string& s, flags f) : version (v) { bool snapshot (!s.empty ()); - check_version (version, snapshot, allow_earliest); + check_version (version, snapshot, f); if (snapshot) { @@ -213,14 +229,23 @@ namespace butl uint64_t sn, std::string si, uint16_t rv, - bool allow_earliest) + flags fl) : epoch (ep), version (vr), snapshot_sn (sn), snapshot_id (move (si)), revision (rv) { - check_version (vr, true, allow_earliest); + check_version (vr, true, fl); + + if (stub ()) + { + if (ep != 0) + throw invalid_argument ("epoch for stub"); + + if (sn != 0) + throw invalid_argument ("snapshot for stub"); + } if (!snapshot_id.empty () && (snapshot_id.size () > 16 || snapshot_sn == 0 || @@ -286,6 +311,9 @@ namespace butl string standard_version:: string_version () const { + if (stub ()) + return "0"; + std::string r (to_string (major ()) + '.' + to_string (minor ()) + '.' + to_string (patch ())); @@ -395,7 +423,8 @@ namespace butl try { - min_version = standard_version (s.substr (p, e - p), true); + min_version = standard_version (s.substr (p, e - p), + standard_version::allow_earliest); } catch (const invalid_argument& e) { @@ -412,7 +441,8 @@ namespace butl try { - max_version = standard_version (s.substr (p, e - p), true); + max_version = standard_version (s.substr (p, e - p), + standard_version::allow_earliest); } catch (const invalid_argument& e) { @@ -460,7 +490,10 @@ namespace butl try { - v = standard_version (s.substr (p), operation != comparison::eq); + v = standard_version (s.substr (p), + operation != comparison::eq + ? standard_version::allow_earliest + : standard_version::none); } catch (const invalid_argument& e) { diff --git a/butl/standard-version.ixx b/butl/standard-version.ixx index a2de26c..c732c20 100644 --- a/butl/standard-version.ixx +++ b/butl/standard-version.ixx @@ -9,8 +9,8 @@ namespace butl std::uint64_t v, const std::string& s, std::uint16_t r, - bool allow_earliest) - : standard_version (v, s, allow_earliest) + flags f) + : standard_version (v, s, f) { // Can't initialize above due to ctor delegating. // @@ -83,4 +83,32 @@ namespace butl { return version % 10000 == 1 && !snapshot (); } + + inline standard_version::flags + operator& (standard_version::flags x, standard_version::flags y) + { + return x &= y; + } + + inline standard_version::flags + operator| (standard_version::flags x, standard_version::flags y) + { + return x |= y; + } + + inline standard_version::flags + operator&= (standard_version::flags& x, standard_version::flags y) + { + return x = static_cast ( + static_cast (x) & + static_cast (y)); + } + + inline standard_version::flags + operator|= (standard_version::flags& x, standard_version::flags y) + { + return x = static_cast ( + static_cast (x) | + static_cast (y)); + } } diff --git a/tests/standard-version/driver.cxx b/tests/standard-version/driver.cxx index 86d9621..677ea8e 100644 --- a/tests/standard-version/driver.cxx +++ b/tests/standard-version/driver.cxx @@ -17,9 +17,11 @@ using namespace butl; // Create standard version from string, and also test another ctors. // static standard_version -version (const string& s, bool allow_earliest = true) +version (const string& s, + standard_version::flags f = + standard_version::allow_earliest | standard_version::allow_stub) { - standard_version r (s, allow_earliest); + standard_version r (s, f); try { @@ -29,7 +31,7 @@ version (const string& s, bool allow_earliest = true) ? r.string_snapshot () : string (), r.revision, - allow_earliest); + f); assert (r == v); @@ -39,12 +41,12 @@ version (const string& s, bool allow_earliest = true) r.snapshot () ? r.string_snapshot () : string (), - allow_earliest); + f); assert (r == v); if (!r.snapshot ()) { - standard_version v (r.version, allow_earliest); + standard_version v (r.version, f); assert (r == v); } } @@ -56,7 +58,7 @@ version (const string& s, bool allow_earliest = true) r.snapshot_sn, r.snapshot_id, r.revision, - allow_earliest); + f); assert (r == v); } diff --git a/tests/standard-version/testscript b/tests/standard-version/testscript index b6bc1a0..6a585a5 100644 --- a/tests/standard-version/testscript +++ b/tests/standard-version/testscript @@ -53,6 +53,13 @@ $* <>EOF 4~1.2.3 EOF + + : stub + : + $* <>EOF + 0 + 0+1 + EOF } : invalid @@ -74,9 +81,9 @@ : $* <'1.2' 2>"'.' expected after minor version" == 1 - : bugfix + : patch : - $* <'1.2.a' 2>'invalid bugfix version' == 1 + $* <'1.2.a' 2>'invalid patch version' == 1 : zero-version : @@ -153,6 +160,8 @@ $* '1.2.3-a.1' >y: final $* '1.2.3-a.0.1' >y: snapshot $* '1.2.3-' >y: earliest + $* '0' >n: stub + } : beta @@ -165,6 +174,7 @@ $* '1.2.3-b.1' >y: final $* '1.2.3-b.0.1' >y: snapshot $* '1.2.3-' >n: earliest + $* '0+1' >n: stub } : compare @@ -201,6 +211,12 @@ $* '1.2.3-a.0.1' '1.2.3-' >'1' : snapshot-gt-earliest $* '1.2.2-b.499.z' '1.2.3-' >'-1' : prev-max-snapshot-lt-earliest } + + : stub + : + { + $* '0+1' '0.0.1-' >'-1' : stub-lt-earliest + } } : constraints @@ -291,6 +307,10 @@ : $* <'==1.2.3-' 2>"invalid version: 'a' or 'b' expected in pre-release" == 1 + : eq-stub + : + $* <'==0' 2>"invalid version: '.' expected after major version" == 1 + : junk : $* <'>= 1.2.3-a.1.1.ads@' 2>'invalid version: junk after version' == 1 -- cgit v1.1