diff --git a/CONTRIBUTORS b/CONTRIBUTORS index ae909e5..1ea747a 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -14,3 +14,4 @@ Jon Brandvein Nathan Herring Laurent Le Brun Dmitry Lomov +Jingwen Chen diff --git a/README.md b/README.md index bfefeda..bd8b873 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ s = shell.quote(p) * [sets](lib/sets.bzl) * [shell](lib/shell.bzl) * [unittest](lib/unittest.bzl) +* [versions](lib/versions.bzl) ## Writing a new module diff --git a/lib.bzl b/lib.bzl index eb3f39a..8e8e6cb 100644 --- a/lib.bzl +++ b/lib.bzl @@ -21,6 +21,7 @@ load("//lib:selects.bzl", "selects") load("//lib:sets.bzl", "sets") load("//lib:shell.bzl", "shell") load("//lib:structs.bzl", "structs") +load("//lib:versions.bzl", "versions") # The unittest module is treated differently to give more convenient names to # the assert functions, while keeping them in the same .bzl file. diff --git a/lib/versions.bzl b/lib/versions.bzl new file mode 100644 index 0000000..b220a18 --- /dev/null +++ b/lib/versions.bzl @@ -0,0 +1,125 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Skylib module containing functions for checking Bazel versions.""" + +def _get_bazel_version(): + """Returns the current Bazel version""" + + return native.bazel_version + + +def _extract_version_number(bazel_version): + """Extracts the semantic version number from a version string + + Args: + bazel_version: the version string that begins with the semantic version + e.g. "1.2.3rc1 abc1234" where "abc1234" is a commit hash. + + Returns: + The semantic version string, like "1.2.3". + """ + for i in range(len(bazel_version)): + c = bazel_version[i] + if not (c.isdigit() or c == "."): + return bazel_version[:i] + return bazel_version + +# Parse the bazel version string from `native.bazel_version`. +# e.g. +# "0.10.0rc1 abc123d" => (0, 10, 0) +# "0.3.0" => (0, 3, 0) +def _parse_bazel_version(bazel_version): + """Parses a version string into a 3-tuple of ints + + int tuples can be compared directly using binary operators (<, >). + + Args: + bazel_version: the Bazel version string + + Returns: + An int 3-tuple of a (major, minor, patch) version. + """ + + version = _extract_version_number(bazel_version) + return tuple([int(n) for n in version.split(".")]) + + +def _is_at_most(threshold, version): + """Check that a version is lower or equals to a threshold. + + Args: + threshold: the maximum version string + version: the version string to be compared to the threshold + + Returns: + True if version <= threshold. + """ + return _parse_bazel_version(version) <= _parse_bazel_version(threshold) + + +def _is_at_least(threshold, version): + """Check that a version is higher or equals to a threshold. + + Args: + threshold: the minimum version string + version: the version string to be compared to the threshold + + Returns: + True if version >= threshold. + """ + + return _parse_bazel_version(version) >= _parse_bazel_version(threshold) + + +def _check_bazel_version(minimum_bazel_version, maximum_bazel_version=None, bazel_version=None): + """Check that the version of Bazel is valid within the specified range. + + Args: + minimum_bazel_version: minimum version of Bazel expected + maximum_bazel_version: maximum version of Bazel expected + bazel_version: the version of Bazel to check. Used for testing, defaults to native.bazel_version + """ + if not bazel_version: + if "bazel_version" not in dir(native): + fail("\nCurrent Bazel version is lower than 0.2.1, expected at least %s\n" % minimum_bazel_version) + elif not native.bazel_version: + print("\nCurrent Bazel is not a release version, cannot check for compatibility.") + print("Make sure that you are running at least Bazel %s.\n" % minimum_bazel_version) + else: + bazel_version = native.bazel_version + + if not _is_at_least( + threshold = minimum_bazel_version, + version = bazel_version): + fail("\nCurrent Bazel version is {}, expected at least {}\n".format( + bazel_version, minimum_bazel_version)) + + if maximum_bazel_version: + max_bazel_version = _parse_bazel_version(maximum_bazel_version) + if not _is_at_most( + threshold = maximum_bazel_version, + version = bazel_version): + fail("\nCurrent Bazel version is {}, expected at most {}\n".format( + bazel_version, maximum_bazel_version)) + + pass + +versions = struct( + get=_get_bazel_version, + parse=_parse_bazel_version, + check=_check_bazel_version, + is_at_most=_is_at_most, + is_at_least=_is_at_least, +) diff --git a/tests/BUILD b/tests/BUILD index 4dd4a46..9df7585 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -5,6 +5,7 @@ load(":selects_tests.bzl", "selects_test_suite") load(":sets_tests.bzl", "sets_test_suite") load(":shell_tests.bzl", "shell_test_suite") load(":structs_tests.bzl", "structs_test_suite") +load(":versions_tests.bzl", "versions_test_suite") licenses(["notice"]) @@ -21,3 +22,5 @@ sets_test_suite() shell_test_suite() structs_test_suite() + +versions_test_suite() diff --git a/tests/versions_tests.bzl b/tests/versions_tests.bzl new file mode 100644 index 0000000..1dc01f4 --- /dev/null +++ b/tests/versions_tests.bzl @@ -0,0 +1,68 @@ +# Copyright 2018 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unit tests for versions.bzl.""" + +load("//:lib.bzl", "versions", "asserts", "unittest") + +def _parse_test(ctx): + """Unit tests for versions.parse""" + env = unittest.begin(ctx) + + asserts.equals(env, (0, 10, 0), versions.parse("0.10.0rc1 abcd123")) + asserts.equals(env, (0, 4, 0), versions.parse("0.4.0 abcd123")) + asserts.equals(env, (0, 4, 0), versions.parse("0.4.0")) + asserts.equals(env, (0, 4, 0), versions.parse("0.4.0rc")) + + unittest.end(env) + +def _version_comparison_test(ctx): + """Unit tests for versions.is_at_least and is_at_most""" + env = unittest.begin(ctx) + + asserts.false(env, versions.is_at_least("0.11.0 123abcd", "0.10.0rc1 abcd123")) + asserts.true(env, versions.is_at_least("0.9.0", "0.10.0rc2")) + asserts.true(env, versions.is_at_least("0.9.0", "0.9.0rc3")) + asserts.true(env, versions.is_at_least("0.9.0", "1.2.3")) + + asserts.false(env, versions.is_at_most("0.4.0 123abcd", "0.10.0rc1 abcd123")) + asserts.true(env, versions.is_at_most("0.4.0", "0.3.0rc2")) + asserts.true(env, versions.is_at_most("0.4.0", "0.4.0rc3")) + asserts.true(env, versions.is_at_most("1.4.0", "0.4.0rc3")) + + unittest.end(env) + +def _check_test(ctx): + """Unit tests for versions.check""" + env = unittest.begin(ctx) + + asserts.equals(env, None, versions.check("0.4.5 abcdef", bazel_version="0.10.0rc1 abcd123")) + asserts.equals(env, None, versions.check("0.4.5", bazel_version="0.4.5")) + asserts.equals(env, None, versions.check("0.4.5", bazel_version="0.10.0rc1 abcd123")) + asserts.equals(env, None, versions.check("0.4.5", maximum_bazel_version="1.0.0", bazel_version="0.10.0rc1 abcd123")) + + unittest.end(env) + +parse_test = unittest.make(_parse_test) +version_comparison_test = unittest.make(_version_comparison_test) +check_test = unittest.make(_check_test) + +def versions_test_suite(): + """Creates the test targets and test suite for versions.bzl tests.""" + unittest.suite( + "versions_tests", + parse_test, + version_comparison_test, + check_test, + )