From f8851063035424e441259af9f26c28af090542b4 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Tue, 29 Jan 2019 16:08:35 +0300 Subject: Add package_manifest constructor that translates package version Also complete ~$/^$ dependency constraints using standard_version_constraint class. --- libbpkg/manifest.cxx | 139 +++++++++++++++++++--------------------------- libbpkg/manifest.hxx | 29 +++++++++- tests/manifest/driver.cxx | 28 +++++++++- tests/manifest/testscript | 127 +++++++++++++++++++++++++++++++----------- 4 files changed, 205 insertions(+), 118 deletions(-) diff --git a/libbpkg/manifest.cxx b/libbpkg/manifest.cxx index 2f98b4c..79e2977 100644 --- a/libbpkg/manifest.cxx +++ b/libbpkg/manifest.cxx @@ -855,10 +855,10 @@ namespace bpkg // The dependent package version can't be empty or earliest. // if (v.empty ()) - throw invalid_argument ("empty version"); + throw invalid_argument ("dependent version is empty"); if (v.release && v.release->empty ()) - throw invalid_argument ("earliest version"); + throw invalid_argument ("dependent version is earliest"); // For the more detailed description of the following semantics refer to // the depends value documentation. @@ -882,79 +882,27 @@ namespace bpkg string vs (v.string ()); - if (optional sv = parse_standard_version (vs)) - try - { - char op (min_open ? '~' : '^'); - standard_version_constraint vc (op + vs); - - // The shortcut operators' representation is a range. - // - assert (vc.min_version && vc.max_version); - - standard_version& mnv (*vc.min_version); - standard_version& mxv (*vc.max_version); - - // For a release adjust the min version endpoint, setting its patch to - // zero. For ^ also set the minor version to zero, unless the major - // version is zero (reduced to ~). - // - if (sv->release ()) - { - mnv = standard_version ( - sv->epoch, - sv->major (), - op == '^' && sv->major () != 0 ? 0 : sv->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 (sv->final () || (sv->snapshot () && sv->patch () != 0)) - { - mnv = standard_version ( - sv->epoch, - sv->major (), - op == '^' && sv->major () != 0 ? 0 : sv->minor (), - 0 /* patch */, - sv->patch () != 0 || (op == '^' && sv->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 (sv->snapshot () && sv->patch () == 0); + // Note that a stub dependent is not allowed for the shortcut operator. + // However, we allow it here to fail later (in + // standard_version_constraint()) with a more precise description. + // + optional sv ( + parse_standard_version (vs, standard_version::allow_stub)); - uint16_t pr (*sv->pre_release ()); + if (!sv) + throw invalid_argument ("dependent version is not standard"); - mnv = standard_version (sv->epoch, - sv->major (), - sv->minor (), - 0 /* patch */, - pr, - 1 /* snapshot_sn */, - "" /* snapshot_id */); + standard_version_constraint vc (min_open ? "~$" : "^$", *sv); - // Note: the max version endpoint is already open. - // - mxv = standard_version (sv->epoch, - sv->major (), - sv->minor (), - 0 /* patch */, - pr + 1); - } + try + { + assert (vc.min_version && vc.max_version); - return dependency_constraint (version (mnv.string ()), vc.min_open, - version (mxv.string ()), vc.max_open); + return dependency_constraint ( + version (vc.min_version->string ()), + vc.min_open, + version (vc.max_version->string ()), + vc.max_open); } catch (const invalid_argument&) { @@ -962,8 +910,6 @@ namespace bpkg // assert (false); } - - throw invalid_argument (vs + " is not a standard version"); } // Calculate effective constraint for a range. @@ -1448,12 +1394,14 @@ namespace bpkg // pkg_package_manifest // static void - parse_package_manifest (parser& p, - name_value nv, - bool iu, - bool cd, - package_manifest_flags fl, - package_manifest& m) + parse_package_manifest ( + parser& p, + name_value nv, + const function& tf, + bool iu, + bool cd, + package_manifest_flags fl, + package_manifest& m) { auto bad_name ([&p, &nv](const string& d) { throw parsing (p.name (), nv.name_line, nv.name_column, d);}); @@ -1560,6 +1508,24 @@ namespace bpkg // if (m.version.release && m.version.release->empty ()) bad_value ("invalid package version release"); + + if (tf) + { + tf (m.version); + + // Re-validate the version after the translation. + // + // The following description will be confusing for the end user. + // However, they shouldn't ever see it unless the translation + // function is broken. + // + if (m.version.empty ()) + bad_value ("empty translated package version"); + + if (m.version.release && m.version.release->empty ()) + bad_value ("invalid translated package version " + + m.version.string () + ": earliest release"); + } } else if (n == "project") { @@ -2007,11 +1973,12 @@ namespace bpkg // package_manifest:: package_manifest (manifest_parser& p, + const function& tf, bool iu, bool cd, package_manifest_flags fl) { - parse_package_manifest (p, p.next (), iu, cd, fl, *this); + parse_package_manifest (p, p.next (), tf, iu, cd, fl, *this); // Make sure this is the end. // @@ -2023,12 +1990,22 @@ namespace bpkg package_manifest:: package_manifest (manifest_parser& p, + bool iu, + bool cd, + package_manifest_flags fl) + : package_manifest (p, function (), iu, cd, fl) + { + } + + package_manifest:: + package_manifest (manifest_parser& p, name_value nv, bool iu, bool cd, package_manifest_flags fl) { - parse_package_manifest (p, move (nv), iu, cd, fl, *this); + parse_package_manifest ( + p, move (nv), function (), iu, cd, fl, *this); } static const string description_file ("description-file"); diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx index 3bce47c..df9247b 100644 --- a/libbpkg/manifest.hxx +++ b/libbpkg/manifest.hxx @@ -322,7 +322,8 @@ namespace bpkg // Return the completed constraint if it refers to the dependent package // version and copy of itself otherwise. Throw std::invalid_argument if // the resulting constraint is invalid (max version is less than min - // version in range, non-standard version for a shortcut operator, etc.). + // version in range, non-standard or latest snapshot version for a + // shortcut operator, etc.). // dependency_constraint effective (version) const; @@ -650,6 +651,28 @@ namespace bpkg package_manifest_flags::forbid_sha256sum | package_manifest_flags::forbid_fragment); + // As above but also call the translate function for the version value + // passing through any exception it may throw. Throw std::invalid_argument + // if the resulting version isn't a valid package version (empty, earliest + // release, etc). + // + // In particular, the translation function may "patch" the version with + // the snapshot information (see for + // details). This translation is normally required for manifests of + // packages that are accessed as directories (as opposed to package + // archives that should have their version already patched). + // + using translate_function = void (version_type&); + + package_manifest (butl::manifest_parser&, + const std::function&, + bool ignore_unknown = false, + bool complete_depends = true, + package_manifest_flags = + package_manifest_flags::forbid_location | + package_manifest_flags::forbid_sha256sum | + package_manifest_flags::forbid_fragment); + // Create an element of the list manifest. // package_manifest (butl::manifest_parser&, @@ -849,8 +872,8 @@ namespace bpkg // it is absolute and the authority or fragment is present. Otherwise // represent it as a local path, appending the fragment if present. // - // - repository_url(string) ctor throws invalid_argument exception for an - // empty string. + // - repository_url(string) ctor throws std::invalid_argument exception for + // an empty string. // using repository_url = butl::basic_url; diff --git a/tests/manifest/driver.cxx b/tests/manifest/driver.cxx index 3bffd4a..5f8a0f5 100644 --- a/tests/manifest/driver.cxx +++ b/tests/manifest/driver.cxx @@ -9,6 +9,7 @@ #include #include +#include #include @@ -86,9 +87,25 @@ main (int argc, char* argv[]) cin.exceptions (ios_base::failbit | ios_base::badbit); if (opt == "-p") - pkg_package_manifest (p, - false /* ignore_unknown */, - complete_depends).serialize (s); + package_manifest ( + p, + [] (version& v) + { + // Emulate populating the snapshot information for the latest + // snapshot. + // + if (butl::optional sv = + parse_standard_version (v.string ())) + { + if (sv->latest_snapshot ()) + { + sv->snapshot_sn = 123; + v = version (sv->string ()); + } + } + }, + false /* ignore_unknown */, + complete_depends).serialize (s); else if (opt == "-pp") pkg_package_manifests (p).serialize (s); else if (opt == "-dp") @@ -112,6 +129,11 @@ main (int argc, char* argv[]) cerr << e << endl; return 1; } + catch (const invalid_argument& e) + { + cerr << e << endl; + return 1; + } return 0; } diff --git a/tests/manifest/testscript b/tests/manifest/testscript index b1b94ed..591ae07 100644 --- a/tests/manifest/testscript +++ b/tests/manifest/testscript @@ -119,21 +119,76 @@ { : complete : - $* -c -p <>EOO - : 1 - name: foo - version: 2.0.0 - summary: Modern C++ parser - license: LGPLv2 - depends: bar == $ | libbaz ~$ | libbox ^$ | libfox [1.0 $) - EOI - : 1 - name: foo - version: 2.0.0 - summary: Modern C++ parser - license: LGPLv2 - depends: bar == 2.0.0 | libbaz ~2.0.0 | libbox ^2.0.0 | libfox [1.0 2.0.0) - EOO + { + : final + : + $* -c -p <>EOO + : 1 + name: foo + version: 2.0.0 + summary: Modern C++ parser + license: LGPLv2 + depends: bar == $ | libbaz ~$ | libbox ^$ | libfox [1.0 $) + EOI + : 1 + name: foo + version: 2.0.0 + summary: Modern C++ parser + license: LGPLv2 + depends: bar == 2.0.0 | libbaz ~2.0.0 | libbox ^2.0.0 | libfox [1.0 2.0.0) + EOO + + : non-standard + : + $* -c -p <>EOO + : 1 + name: foo + version: 2.0.0-x + summary: Modern C++ parser + license: LGPLv2 + depends: bar == $ | libfox [1.0 $) + EOI + : 1 + name: foo + version: 2.0.0-x + summary: Modern C++ parser + license: LGPLv2 + depends: bar == 2.0.0-x | libfox [1.0 2.0.0-x) + EOO + + : non-standard-shortcut + : + $* -c -p <>EOE != 0 + : 1 + name: foo + version: 2.0.0-x + summary: Modern C++ parser + license: LGPLv2 + depends: bar ~$ + EOI + stdin:6:10: error: invalid dependency constraint: dependent version is not standard + EOE + + : latest-snapshot + : + $* -c -p <>EOO + : 1 + name: foo + version: 2.0.0-a.0.z + summary: Modern C++ parser + license: LGPLv2 + depends: bar == $ | libbaz ~$ | libbox ^$ | libfox [1.0 $) + EOI + : 1 + name: foo + version: 2.0.0-a.0.123 + summary: Modern C++ parser + license: LGPLv2 + depends: bar == 2.0.0-a.0.123 | libbaz [2.0.0-a.0.1 2.0.0-a.1) | libbox\ + [2.0.0-a.0.1 2.0.0-a.1) | libfox [1.0 2.0.0-a.0.123) + EOO + + } : incomplete : @@ -579,20 +634,22 @@ : effective-constraints : { + test.options += -ec + : regular : - $* -ec '1.2.3+1' <>EOO + $* '1.2.3+1' <>EOO [1.0 $) [1.0 $] - [$ 1.2.4) [$ 1.2.4] + ($ 1.2.4] == $ >= $ EOI [1.0 $) [1.0 1.2.3) [1.0 $] [1.0 1.2.3] - [$ 1.2.4) [1.2.3 1.2.4) [$ 1.2.4] [1.2.3 1.2.4] + ($ 1.2.4] (1.2.3 1.2.4] == $ == 1.2.3 >= $ >= 1.2.3 EOO @@ -603,8 +660,8 @@ : final : { - $* -ec '1.2.3+1' <'~$' >'~$ ~1.2.0' : tilda - $* -ec '1.2.3+1' <'^$' >'^$ ^1.0.0' : carrot + $* '1.2.3+1' <'~$' >'~$ ~1.2.0' : tilda + $* '1.2.3+1' <'^$' >'^$ ^1.0.0' : carrot } : pre-release @@ -613,16 +670,16 @@ : tilda : { - $* -ec '1.2.0-b.2' <'~$' >'~$ ~1.2.0-a.1' : no-final - $* -ec '1.2.1-a.1' <'~$' >'~$ ~1.2.0' : final-patch + $* '1.2.0-b.2' <'~$' >'~$ ~1.2.0-a.1' : no-final + $* '1.2.1-a.1' <'~$' >'~$ ~1.2.0' : final-patch } : carrot : { - $* -ec '1.0.0-b.2' <'^$' >'^$ ^1.0.0-a.1' : no-final - $* -ec '1.0.1-a.1' <'^$' >'^$ ^1.0.0' : final-patch - $* -ec '1.1.0-b.2' <'^$' >'^$ ^1.0.0' : final-minor + $* '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 } } @@ -632,18 +689,26 @@ : tilda : { - $* -ec '1.2.1-a.2.345' <'~$' >'~$ ~1.2.0' : patch - $* -ec '1.2.0-a.0.345' <'~$' >'~$ [1.2.0-a.0.1 1.2.0-a.1)' : minor - $* -ec '1.0.0-a.0.345' <'~$' >'~$ [1.0.0-a.0.1 1.0.0-a.1)' : major + $* '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 : { - $* -ec '1.2.1-a.2.345' <'^$' >'^$ ^1.0.0' : patch - $* -ec '1.2.0-a.0.345' <'^$' >'^$ [1.2.0-a.0.1 1.2.0-a.1)' : minor - $* -ec '1.0.0-a.0.345' <'^$' >'^$ [1.0.0-a.0.1 1.0.0-a.1)' : major + $* '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.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 + } } } -- cgit v1.1