aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2020-08-04 23:15:09 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2020-08-12 11:55:44 +0300
commit4791ec7dbb5116a26df896d0b38cd3a66ac64c95 (patch)
treec33c70f5c15ec9988dfcd0e2988668c1969fe05d
parent46c7d448f27e8fc213c50cd241914b2d94e2e308 (diff)
Add standard-version utility
-rw-r--r--libbutl/.gitignore1
-rw-r--r--libbutl/buildfile7
-rw-r--r--libbutl/standard-version.bash.in46
-rw-r--r--libbutl/standard-version.cxx127
-rw-r--r--tests/standard-version/buildfile6
-rw-r--r--tests/standard-version/driver.in8
-rw-r--r--tests/standard-version/testscript20
7 files changed, 214 insertions, 1 deletions
diff --git a/libbutl/.gitignore b/libbutl/.gitignore
index 6b2eb75..02399ae 100644
--- a/libbutl/.gitignore
+++ b/libbutl/.gitignore
@@ -1 +1,2 @@
manifest
+standard-version
diff --git a/libbutl/buildfile b/libbutl/buildfile
index 8039035..135733c 100644
--- a/libbutl/buildfile
+++ b/libbutl/buildfile
@@ -9,7 +9,12 @@ bash{utility}: in{utility}
bash{manifest-parser}: in{manifest-parser} bash{utility} exe{manifest}
bash{manifest-serializer}: in{manifest-serializer} bash{utility} exe{manifest}
-exe{manifest}: cxx{manifest} $libs
+bash{standard-version}: in{standard-version} \
+ bash{utility} \
+ exe{standard-version}
+
+exe{manifest}: cxx{manifest} $libs
+exe{standard-version}: cxx{standard-version} $libs
# Install our binding support executables next to the modules.
#
diff --git a/libbutl/standard-version.bash.in b/libbutl/standard-version.bash.in
new file mode 100644
index 0000000..20215d9
--- /dev/null
+++ b/libbutl/standard-version.bash.in
@@ -0,0 +1,46 @@
+# file : libbutl/standard-version.bash.in
+# license : MIT; see accompanying LICENSE file
+
+if [ "$butl_standard_version" ]; then
+ return 0
+else
+ butl_standard_version=true
+fi
+
+@import libbutl/utility@
+
+# Check that the specified predicates are true for a standard version.
+#
+# Parse the standard version and exit with code 0 if all the specified
+# predicates are true and code 1 otherwise. If the specified argument is not a
+# valid standard version, then print the error description to stderr, unless
+# --is-version option is specified, and exit with code 2. For other errors
+# (usage, etc), issue diagnostics and exit with code 3.
+#
+# Options in the --is-[not-]<predicate> form enable the standard version
+# predicate checks. The recognized predicates are:
+#
+# snapshot
+# latest-snapshot
+# alpha
+# beta
+# pre-release
+# release
+# final
+# stub
+# earliest
+#
+# Note that specifying opposite predicates (for example, --is-beta and
+# --is-not-beta) is not a usage error and results in exit code 1. See
+# libbutl/standard-version.mxx for the predicates semantics.
+#
+# Other options:
+#
+# --is-version
+#
+# Don't issue diagnostics if the argument is not a valid standard version.
+#
+function butl_standard_version () # [<options>] <standard-version>
+{
+ "$(butl_path)/standard-version" "$@"
+}
diff --git a/libbutl/standard-version.cxx b/libbutl/standard-version.cxx
new file mode 100644
index 0000000..36e79fb
--- /dev/null
+++ b/libbutl/standard-version.cxx
@@ -0,0 +1,127 @@
+// file : libbutl/standard-version.cxx
+// license : MIT; see accompanying LICENSE file
+
+#include <string>
+#include <cstddef> // size_t
+#include <iostream>
+#include <stdexcept> // invalid_argument
+
+#include <libbutl/utility.mxx> // operator<<(ostream,exception)
+#include <libbutl/optional.mxx>
+#include <libbutl/standard-version.mxx>
+
+using namespace std;
+using namespace butl;
+
+// Usage: argv[0] [<options>] <standard-version>
+//
+// Check that the specified predicates are true for a standard version.
+//
+// See standard-version.bash.in for the functionality and options description.
+//
+int
+main (int argc, char* argv[])
+{
+ using butl::optional;
+
+ // We could probably try to parse the last argument as a version first and
+ // then evaluate the predicates while parsing the options. This, however
+ // would worsen the diagnostics (think about the `standard-version --beta`
+ // command) and would complicate things a bit, since at the time of the
+ // version parsing we wouldn't know if to print the diagnostics for an
+ // invalid version. Thus, we will parse the arguments in the direct order.
+ //
+ optional<bool> snapshot;
+ optional<bool> latest_sn;
+ optional<bool> alpha;
+ optional<bool> beta;
+ optional<bool> pre_release;
+ optional<bool> release;
+ optional<bool> final;
+ optional<bool> stub;
+ optional<bool> earliest;
+
+ bool valid (false); // --is-version is specified.
+ bool opposite (false); // Opposite predicates are specified simultaneously
+ // (i.e. --is-beta and --is-not-beta).
+
+ int i (1);
+ for (; i != argc; ++i)
+ {
+ string op (argv[i]);
+
+ bool is;
+ size_t n;
+
+ if (op.compare (0, (n = 9), "--is-not-") == 0) is = false;
+ else if (op.compare (0, (n = 5), "--is-") == 0) is = true;
+ else break;
+
+ string p (op, n);
+
+ // Set the predicate expected value. If the opposite value have already
+ // been specified, then save this information to exit with code 1 later
+ // (note that we still need to validate the rest of the arguments).
+ //
+ auto set = [is, &opposite] (optional<bool>& v)
+ {
+ if (!v)
+ v = is;
+ else if (*v != is)
+ opposite = true;
+ };
+
+ if (p == "snapshot") set (snapshot);
+ else if (p == "latest-snapshot") set (latest_sn);
+ else if (p == "alpha") set (alpha);
+ else if (p == "beta") set (beta);
+ else if (p == "pre-release") set (pre_release);
+ else if (p == "release") set (release);
+ else if (p == "final") set (final);
+ else if (p == "stub") set (stub);
+ else if (p == "earliest") set (earliest);
+ else if (p == "version" && is) valid = true;
+ else break;
+ }
+
+ if (i == argc)
+ {
+ cerr << "error: missing version" << endl;
+ return 3;
+ }
+
+ string a (argv[i++]);
+
+ if (i != argc)
+ {
+ cerr << "error: unexpected argument '" << argv[i] << "'" << endl;
+ return 3;
+ }
+
+ try
+ {
+ standard_version v (a,
+ (standard_version::allow_earliest |
+ standard_version::allow_stub));
+
+ return !opposite &&
+ (!snapshot || *snapshot == v.snapshot()) &&
+ (!latest_sn || *latest_sn == v.latest_snapshot ()) &&
+ (!alpha || *alpha == v.alpha ().has_value ()) &&
+ (!beta || *beta == v.beta ().has_value ()) &&
+ (!pre_release || *pre_release == v.pre_release ().has_value ()) &&
+ (!release || *release == v.release ()) &&
+ (!final || *final == v.final ()) &&
+ (!stub || *stub == v.stub ()) &&
+ (!earliest || *earliest == v.earliest ())
+ ? 0
+ : 1;
+ }
+ catch (const invalid_argument& e)
+ {
+ if (!valid)
+ cerr << "error: " << e << endl;
+
+ return 2;
+ }
+}
diff --git a/tests/standard-version/buildfile b/tests/standard-version/buildfile
new file mode 100644
index 0000000..5fe61e5
--- /dev/null
+++ b/tests/standard-version/buildfile
@@ -0,0 +1,6 @@
+# file : tests/standard-version/buildfile
+# license : MIT; see accompanying LICENSE file
+
+import mods = libbutl.bash%bash{standard-version}
+
+exe{driver}: in{driver} $mods testscript
diff --git a/tests/standard-version/driver.in b/tests/standard-version/driver.in
new file mode 100644
index 0000000..f1df612
--- /dev/null
+++ b/tests/standard-version/driver.in
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+# file : tests/standard-version/driver.in
+# license : MIT; see accompanying LICENSE file
+
+@import libbutl/standard-version@
+
+butl_standard_version "$@"
diff --git a/tests/standard-version/testscript b/tests/standard-version/testscript
new file mode 100644
index 0000000..4ee17cc
--- /dev/null
+++ b/tests/standard-version/testscript
@@ -0,0 +1,20 @@
+# file : tests/standard-version/testscript
+# license : MIT; see accompanying LICENSE file
+
+: invalid-arguments
+:
+{
+ $* --is-snapshot 2>'error: missing version' == 3 : no-ver
+ $* '1.2.3' '1.0' 2>"error: unexpected argument '1.0'" == 3 : unexpected
+ $* '1.2' 2>"error: '.' expected after minor version" == 2 : no-minor
+}
+
+$* --is-snapshot '1.2.3-a.1.12345.abcdef' == 0 : snapshot-yes
+$* --is-snapshot '1.2.3-a.1' == 1 : snapshot-no
+$* --is-not-snapshot '1.2.3-a.1' == 0 : not-snapshot-yes
+$* --is-not-snapshot '1.2.3-a.1.12345.abcdef' == 1 : not-snapshot-no
+$* --is-version 'abc' == 2 : version-no
+
+$* --is-not-final --is-alpha '1.2.3-a.1.12345.abcdef' == 0 : not-final-alpha
+$* --is-beta --is-latest-snapshot '1.2.3-b.1.z' == 0 : beta-latest-snapshot
+$* --is-not-beta --is-beta '1.2.3-b.1.z' == 1 : opposite