From a6bf026d143b559ac0358dfd4b89f317901fd02e Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 30 Aug 2019 20:55:55 +0300 Subject: Make version revision optional --- libbpkg/manifest.cxx | 62 +++++++++++++++++++++++++++++++++++++--------------- libbpkg/manifest.hxx | 47 +++++++++++++++++++++++++++++---------- 2 files changed, 80 insertions(+), 29 deletions(-) (limited to 'libbpkg') diff --git a/libbpkg/manifest.cxx b/libbpkg/manifest.cxx index bba616f..19f24ab 100644 --- a/libbpkg/manifest.cxx +++ b/libbpkg/manifest.cxx @@ -162,7 +162,7 @@ namespace bpkg version (uint16_t e, std::string u, optional l, - uint16_t r, + optional r, uint32_t i) : epoch (e), upstream (move (u)), @@ -170,11 +170,14 @@ namespace bpkg revision (r), iteration (i), canonical_upstream ( - data_type (upstream.c_str (), data_type::parse::upstream). + data_type (upstream.c_str (), + data_type::parse::upstream, + false /* fold_zero_revision */). canonical_upstream), canonical_release ( data_type (release ? release->c_str () : nullptr, - data_type::parse::release). + data_type::parse::release, + false /* fold_zero_revision */). canonical_release) { // Check members constrains. @@ -187,7 +190,7 @@ namespace bpkg if (!release || !release->empty ()) throw invalid_argument ("not-empty release for empty version"); - if (revision != 0) + if (revision) throw invalid_argument ("revision for empty version"); if (iteration != 0) @@ -196,7 +199,7 @@ namespace bpkg // Empty release signifies the earliest possible release. Revision and/or // iteration are meaningless in such a context. // - else if (release && release->empty () && (revision != 0 || iteration != 0)) + else if (release && release->empty () && (revision || iteration != 0)) throw invalid_argument ("revision for earliest possible release"); } @@ -251,8 +254,11 @@ namespace bpkg } version::data_type:: - data_type (const char* v, parse pr): revision (0) + data_type (const char* v, parse pr, bool fold_zero_rev) { + if (fold_zero_rev) + assert (pr == parse::full); + // Otherwise compiler gets confused with string() member. // using std::string; @@ -418,7 +424,10 @@ namespace bpkg if (lnn >= cb) // Contains non-digits. bad_arg ("revision should be 2-byte unsigned integer"); - revision = uint16 (cb, "revision"); + std::uint16_t rev (uint16 (cb, "revision")); + + if (rev != 0 || !fold_zero_rev) + revision = rev; } else if (cb != p) { @@ -489,7 +498,7 @@ namespace bpkg canonical_upstream.empty () && canonical_release.empty ()) { - assert (revision == 0); // Can't happen if through all previous checks. + assert (!revision); // Can't happen if through all previous checks. bad_arg ("empty version"); } } @@ -534,10 +543,10 @@ namespace bpkg if (!ignore_revision) { - if (revision != 0) + if (revision) { v += '+'; - v += to_string (revision); + v += to_string (*revision); } if (!ignore_iteration && iteration != 0) @@ -646,7 +655,7 @@ namespace bpkg if (mnv != "$") try { - min_version = version (mnv); + min_version = version (mnv, false /* fold_zero_revision */); } catch (const invalid_argument& e) { @@ -674,7 +683,7 @@ namespace bpkg if (mxv != "$") try { - max_version = version (mxv); + max_version = version (mxv, false /* fold_zero_revision */); } catch (const invalid_argument& e) { @@ -735,6 +744,11 @@ namespace bpkg { assert (vc.min_version && vc.max_version); + // Note that standard_version::string() folds the zero revision. + // However, that's ok since the shortcut operator ~X.Y.Z+0 + // translates into [X.Y.Z+0 X.Y+1.0-) which covers the same versions + // set as [X.Y.Z X.Y+1.0-). + // *this = dependency_constraint ( version (vc.min_version->string ()), vc.min_open, @@ -789,7 +803,7 @@ namespace bpkg // version. // if (vs != "$") - v = version (vs); + v = version (vs, false /* fold_zero_revision */); switch (operation) { @@ -844,7 +858,19 @@ namespace bpkg // max version. // if (*min_version > *max_version && !mxe) - throw invalid_argument ("min version is greater than max version"); + { + // Handle the (X+Y X] and [X+Y X] corner cases (any revision of + // version X greater (or equal) than X+Y). Note that technically + // X+Y > X (see version::compare() for details). + // + // Also note that we reasonably fail for (X+Y X) and [X+Y X). + // + if (!(!max_open && + !max_version->revision && + max_version->compare (*min_version, + true /* ignore_revision */) == 0)) + throw invalid_argument ("min version is greater than max version"); + } if (*min_version == *max_version) { @@ -884,7 +910,7 @@ namespace bpkg v = version (v.epoch, move (v.upstream), move (v.release), - 0 /* revision */, + nullopt /* revision */, 0 /* iteration */); // Calculate effective constraint for a shortcut operator. @@ -1601,7 +1627,7 @@ namespace bpkg return email (move (v), move (c)); } - const version stub_version (0, "0", nullopt, 0, 0); + const version stub_version (0, "0", nullopt, nullopt, 0); static void parse_package_manifest ( @@ -1747,7 +1773,7 @@ namespace bpkg try { - m.version = version (move (v)); + m.version = version (v); } catch (const invalid_argument& e) { @@ -2104,7 +2130,7 @@ namespace bpkg // nv = move (*upstream_version); - if (m.version.compare (stub_version, true) == 0) + if (m.version.compare (stub_version, true /* ignore_revision */) == 0) bad_name ("upstream package version specified for a stub"); m.upstream_version = move (nv.value); diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx index c0901e9..3d02e50 100644 --- a/libbpkg/manifest.hxx +++ b/libbpkg/manifest.hxx @@ -42,7 +42,13 @@ namespace bpkg const std::uint16_t epoch; const std::string upstream; const butl::optional release; - const std::uint16_t revision; + + // The absent revision semantics depends on the context the version object + // is used in. Normally, it is equivalent to zero revision but may have a + // special meaning, for example, denoting any package revision. + // + const butl::optional revision; + const std::uint32_t iteration; // Upstream part canonical representation. @@ -56,29 +62,37 @@ namespace bpkg // Create a special empty version. It is less than any other valid // version (and is conceptually equivalent to +0-0-). // - version (): epoch (0), release (""), revision (0), iteration (0) {} + version (): epoch (0), release (""), iteration (0) {} - // Throw std::invalid_argument if the passed string is not a valid - // version representation. + // By default, treat the zero revision as no revision. Throw + // std::invalid_argument if the passed string is not a valid version + // representation. // explicit - version (const std::string& v): version (v.c_str ()) {} + version (const std::string& v, bool fold_zero_revision = true) + : version (v.c_str (), fold_zero_revision) {} explicit - version (const char* v): version (data_type (v, data_type::parse::full)) {} + version (const char* v, bool fold_zero_revision = true) + : version (data_type (v, data_type::parse::full, fold_zero_revision)) + { + } // Create the version object from separate epoch, upstream, release, // revision, and iteration parts. // // Note that it is possible (and legal) to create the special empty - // version via this interface as version(0, string(), string(), 0, 0). + // version via this interface as version(0, string(), string(), nullopt, 0). // version (std::uint16_t epoch, std::string upstream, butl::optional release, - std::uint16_t revision, + butl::optional revision, std::uint32_t iteration); + std::uint16_t + effective_revision () const noexcept {return revision ? *revision : 0;} + version (version&&) = default; version (const version&) = default; version& operator= (version&&); @@ -145,7 +159,7 @@ namespace bpkg assert (!e || (epoch == 0 && release && release->empty () && - revision == 0 && iteration == 0)); + !revision && iteration == 0)); return e; } @@ -155,7 +169,7 @@ namespace bpkg { enum class parse {full, upstream, release}; - data_type (const char*, parse); + data_type (const char*, parse, bool fold_zero_revision); // Note that there is no iteration component as it can't be present in // the string representation passed to the ctor. @@ -163,7 +177,7 @@ namespace bpkg std::uint16_t epoch; std::string upstream; butl::optional release; - std::uint16_t revision; + butl::optional revision; std::string canonical_upstream; std::string canonical_release; }; @@ -305,6 +319,13 @@ namespace bpkg // `~$` (min endpoint is open) or `^$` (max endpoint is open). Note that // equal endpoints can never be both open. // + // An absent endpoint version revision has the 'any revision' meaning and + // so translates into the effective revision differently, depending on the + // range endpoint side and openness: + // + // [X Y) == [X+0 Y+0) + // (X Y] == (X+max Y+max] + // class LIBBPKG_EXPORT dependency_constraint { public: @@ -313,11 +334,15 @@ namespace bpkg bool min_open; bool max_open; + // Preserve the zero endpoint version revisions (see above for details). + // + explicit dependency_constraint (const std::string&); dependency_constraint (butl::optional min_version, bool min_open, butl::optional max_version, bool max_open); + explicit dependency_constraint (const version& v) : dependency_constraint (v, false, v, false) {} -- cgit v1.1