aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2019-01-29 16:08:35 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2019-01-30 15:53:10 +0300
commitf8851063035424e441259af9f26c28af090542b4 (patch)
tree8488701f06909d6d9bce3cb3a964bce3116e6f8e
parentec1f115eaed9ec950cacab1b7ded6d35e99afb9d (diff)
Add package_manifest constructor that translates package version
Also complete ~$/^$ dependency constraints using standard_version_constraint class.
-rw-r--r--libbpkg/manifest.cxx139
-rw-r--r--libbpkg/manifest.hxx29
-rw-r--r--tests/manifest/driver.cxx28
-rw-r--r--tests/manifest/testscript127
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<standard_version> 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<standard_version> 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<package_manifest::translate_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<translate_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<translate_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<translate_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 <libbutl/standard-version.mxx> 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<translate_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<repository_protocol,
repository_url_traits>;
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 <libbutl/manifest-parser.mxx>
#include <libbutl/manifest-serializer.mxx>
+#include <libbutl/standard-version.mxx>
#include <libbpkg/manifest.hxx>
@@ -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<standard_version> 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 <<EOI >>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 <<EOI >>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 <<EOI >>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 <<EOI 2>>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 <<EOI >>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' <<EOI >>EOO
+ $* '1.2.3+1' <<EOI >>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
+ }
}
}