diff options
-rw-r--r-- | libbutl/.gitignore | 1 | ||||
-rw-r--r-- | libbutl/buildfile | 7 | ||||
-rw-r--r-- | libbutl/standard-version.bash.in | 46 | ||||
-rw-r--r-- | libbutl/standard-version.cxx | 127 | ||||
-rw-r--r-- | tests/standard-version/buildfile | 6 | ||||
-rw-r--r-- | tests/standard-version/driver.in | 8 | ||||
-rw-r--r-- | tests/standard-version/testscript | 20 |
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 |