aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/BUILD5
-rw-r--r--lib/dicts.bzl27
-rw-r--r--lib/subpackages.bzl96
-rw-r--r--lib/unittest.bzl40
-rw-r--r--lib/versions.bzl5
5 files changed, 168 insertions, 5 deletions
diff --git a/lib/BUILD b/lib/BUILD
index 7e7a9a4..2328081 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -62,6 +62,11 @@ bzl_library(
)
bzl_library(
+ name = "subpackages",
+ srcs = ["subpackages.bzl"],
+)
+
+bzl_library(
name = "types",
srcs = ["types.bzl"],
)
diff --git a/lib/dicts.bzl b/lib/dicts.bzl
index 3f8e661..5a92aa1 100644
--- a/lib/dicts.bzl
+++ b/lib/dicts.bzl
@@ -38,6 +38,33 @@ def _add(*dictionaries, **kwargs):
result.update(kwargs)
return result
+def _omit(dictionary, keys):
+ """Returns a new `dict` that has all the entries of `dictionary` with keys not in `keys`.
+
+ Args:
+ dictionary: A `dict`.
+ keys: A sequence.
+
+ Returns:
+ A new `dict` that has all the entries of `dictionary` with keys not in `keys`.
+ """
+ keys_set = {k: None for k in keys}
+ return {k: dictionary[k] for k in dictionary if k not in keys_set}
+
+def _pick(dictionary, keys):
+ """Returns a new `dict` that has all the entries of `dictionary` with keys in `keys`.
+
+ Args:
+ dictionary: A `dict`.
+ keys: A sequence.
+
+ Returns:
+ A new `dict` that has all the entries of `dictionary` with keys in `keys`.
+ """
+ return {k: dictionary[k] for k in keys if k in dictionary}
+
dicts = struct(
add = _add,
+ omit = _omit,
+ pick = _pick,
)
diff --git a/lib/subpackages.bzl b/lib/subpackages.bzl
new file mode 100644
index 0000000..5b674fd
--- /dev/null
+++ b/lib/subpackages.bzl
@@ -0,0 +1,96 @@
+# Copyright 2022 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 common functions for working with native.subpackages()
+"""
+_SUBPACKAGES_SUPPORTED = hasattr(native, "subpackages")
+
+def _supported():
+ return _SUBPACKAGES_SUPPORTED
+
+def _check_supported():
+ if not _SUBPACKAGES_SUPPORTED:
+ fail("native.subpackages not supported in this version of Bazel.")
+
+def _all(exclude = [], allow_empty = False, fully_qualified = True):
+ """List all direct subpackages of the current package regardless of directory depth.
+
+ The returned list contains all subpackages, but not subpackages of subpackages.
+
+ Example:
+ Assuming the following BUILD files exist:
+
+ BUILD
+ foo/BUILD
+ foo/sub/BUILD
+ bar/BUILD
+ baz/deep/dir/BUILD
+
+ If the current package is '//' all() will return ['//foo', '//bar',
+ '//baz/deep/dir']. //foo/sub is not included because it is a direct
+ subpackage of '//foo' not '//'
+
+ NOTE: fail()s if native.subpackages() is not supported.
+
+ Args:
+ exclude: see native.subpackages(exclude)
+ allow_empty: see native.subpackages(allow_empty)
+ fully_qualified: It true return fully qualified Labels for subpackages,
+ otherwise returns subpackage path relative to current package.
+
+ Returns:
+ A mutable sorted list containing all sub-packages of the current Bazel
+ package.
+ """
+ _check_supported()
+
+ subs = native.subpackages(include = ["**"], exclude = exclude, allow_empty = allow_empty)
+ if fully_qualified:
+ return [_fully_qualified(s) for s in subs]
+
+ return subs
+
+def _fully_qualified(relative_path):
+ return "//%s/%s" % (native.package_name(), relative_path)
+
+def _exists(relative_path):
+ """Checks to see if relative_path is a direct subpackage of the current package.
+
+ Example:
+
+ BUILD
+ foo/BUILD
+ foo/sub/BUILD
+
+ If the current package is '//' (the top-level BUILD file):
+ subpackages.exists("foo") == True
+ subpackages.exists("foo/sub") == False
+ subpackages.exists("bar") == False
+
+ NOTE: fail()s if native.subpackages() is not supported in the current Bazel version.
+
+ Args:
+ relative_path: a path to a subpackage to test, must not be an absolute Label.
+
+ Returns:
+ True if 'relative_path' is a subpackage of the current package.
+ """
+ _check_supported()
+ return relative_path in native.subpackages(include = [relative_path], allow_empty = True)
+
+subpackages = struct(
+ all = _all,
+ exists = _exists,
+ supported = _supported,
+)
diff --git a/lib/unittest.bzl b/lib/unittest.bzl
index 3757b08..02e374c 100644
--- a/lib/unittest.bzl
+++ b/lib/unittest.bzl
@@ -14,9 +14,18 @@
"""Unit testing support.
-Unlike most Skylib files, this exports two modules: `unittest` which contains
-functions to declare and define unit tests, and `asserts` which contains the
-assertions used to within tests.
+Unlike most Skylib files, this exports four modules:
+* `unittest` contains functions to declare and define unit tests for ordinary
+ Starlark functions;
+* `analysistest` contains functions to declare and define tests for analysis
+ phase behavior of a rule, such as a given target's providers or registered
+ actions;
+* `loadingtest` contains functions to declare and define tests for loading
+ phase behavior, such as macros and `native.*`;
+* `asserts` contains the assertions used within tests.
+
+See https://bazel.build/extending/concepts for background about macros, rules,
+and the different phases of a build.
"""
load(":new_sets.bzl", new_sets = "sets")
@@ -139,7 +148,7 @@ def _impl_function_name(impl):
impl_name = impl_name.partition("<function ")[-1]
return impl_name.rpartition(">")[0]
-def _make(impl, attrs = {}):
+def _make(impl, attrs = {}, doc = ""):
"""Creates a unit test rule from its implementation function.
Each unit test is defined in an implementation function that must then be
@@ -169,6 +178,7 @@ def _make(impl, attrs = {}):
impl: The implementation function of the unit test.
attrs: An optional dictionary to supplement the attrs passed to the
unit test's `rule()` constructor.
+ doc: A description of the rule that can be extracted by documentation generating tools.
Returns:
A rule definition that should be stored in a global whose name ends in
@@ -179,6 +189,7 @@ def _make(impl, attrs = {}):
return rule(
impl,
+ doc = doc,
attrs = attrs,
_skylark_testable = True,
test = True,
@@ -364,6 +375,25 @@ def _begin(ctx):
"""
return struct(ctx = ctx, failures = [])
+def _begin_analysis_test(ctx):
+ """Begins an analysis test.
+
+ This should be the first function called in an analysis test implementation
+ function. It initializes a "test environment" that is used to collect
+ assertion failures so that they can be reported and logged at the end of the
+ test.
+
+ Args:
+ ctx: The Starlark context. Pass the implementation function's `ctx` argument
+ in verbatim.
+
+ Returns:
+ A test environment struct that must be passed to assertions and finally to
+ `analysistest.end`. Do not rely on internal details about the fields in this
+ struct as it may change.
+ """
+ return struct(ctx = ctx, failures = [])
+
def _end_analysis_test(env):
"""Ends an analysis test and logs the results.
@@ -647,7 +677,7 @@ unittest = struct(
analysistest = struct(
make = _make_analysis_test,
- begin = _begin,
+ begin = _begin_analysis_test,
end = _end_analysis_test,
fail = _fail,
target_actions = _target_actions,
diff --git a/lib/versions.bzl b/lib/versions.bzl
index 0209a6f..87ca73c 100644
--- a/lib/versions.bzl
+++ b/lib/versions.bzl
@@ -44,6 +44,9 @@ def _parse_bazel_version(bazel_version):
int tuples can be compared directly using binary operators (<, >).
+ For a development build of Bazel, this returns an unspecified version tuple
+ that compares higher than any released version.
+
Args:
bazel_version: the Bazel version string
@@ -52,6 +55,8 @@ def _parse_bazel_version(bazel_version):
"""
version = _extract_version_number(bazel_version)
+ if not version:
+ return (999999, 999999, 999999)
return tuple([int(n) for n in version.split(".")])
def _is_at_most(threshold, version):