feat: add some paths and utils (#1)
* feat: add some paths and utils * test: exercise relative to_label * chore: cleanup naming and rctx helper
This commit is contained in:
parent
1c5ab3dad8
commit
cb8c2bad22
|
@ -10,6 +10,8 @@ load("@io_bazel_stardoc//stardoc:stardoc.bzl", "stardoc")
|
|||
_DOCS = {
|
||||
"//lib:expand_make_vars.bzl": "expand_make_vars.md",
|
||||
"//lib:params_file.bzl": "params_file.md",
|
||||
"//lib:utils.bzl": "utils.md",
|
||||
"//lib:paths.bzl": "paths.md",
|
||||
}
|
||||
|
||||
[
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<!-- Generated with Stardoc: http://skydoc.bazel.build -->
|
||||
|
||||
Public API
|
||||
|
||||
<a id="#relative_file"></a>
|
||||
|
||||
## relative_file
|
||||
|
||||
<pre>
|
||||
relative_file(<a href="#relative_file-to_file">to_file</a>, <a href="#relative_file-frm_file">frm_file</a>)
|
||||
</pre>
|
||||
|
||||
Resolves a relative path between two files, "to_file" and "frm_file", they must share the same root
|
||||
|
||||
**PARAMETERS**
|
||||
|
||||
|
||||
| Name | Description | Default Value |
|
||||
| :------------- | :------------- | :------------- |
|
||||
| <a id="relative_file-to_file"></a>to_file | the path with file name to resolve to, from frm | none |
|
||||
| <a id="relative_file-frm_file"></a>frm_file | the path with file name to resolve from | none |
|
||||
|
||||
**RETURNS**
|
||||
|
||||
The relative path from frm_file to to_file, including the file name
|
||||
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
<!-- Generated with Stardoc: http://skydoc.bazel.build -->
|
||||
|
||||
Public API
|
||||
|
||||
<a id="#glob_directories"></a>
|
||||
|
||||
## glob_directories
|
||||
|
||||
<pre>
|
||||
glob_directories(<a href="#glob_directories-include">include</a>, <a href="#glob_directories-kwargs">kwargs</a>)
|
||||
</pre>
|
||||
|
||||
|
||||
|
||||
**PARAMETERS**
|
||||
|
||||
|
||||
| Name | Description | Default Value |
|
||||
| :------------- | :------------- | :------------- |
|
||||
| <a id="glob_directories-include"></a>include | <p align="center"> - </p> | none |
|
||||
| <a id="glob_directories-kwargs"></a>kwargs | <p align="center"> - </p> | none |
|
||||
|
||||
|
||||
<a id="#is_external_label"></a>
|
||||
|
||||
## is_external_label
|
||||
|
||||
<pre>
|
||||
is_external_label(<a href="#is_external_label-param">param</a>)
|
||||
</pre>
|
||||
|
||||
Returns True if the given Label (or stringy version of a label) represents a target outside of the workspace
|
||||
|
||||
**PARAMETERS**
|
||||
|
||||
|
||||
| Name | Description | Default Value |
|
||||
| :------------- | :------------- | :------------- |
|
||||
| <a id="is_external_label-param"></a>param | a string or label | none |
|
||||
|
||||
**RETURNS**
|
||||
|
||||
a bool
|
||||
|
||||
|
||||
<a id="#path_to_workspace_root"></a>
|
||||
|
||||
## path_to_workspace_root
|
||||
|
||||
<pre>
|
||||
path_to_workspace_root()
|
||||
</pre>
|
||||
|
||||
Retuns the path to the workspace root under bazel
|
||||
|
||||
|
||||
**RETURNS**
|
||||
|
||||
Path to the workspace root
|
||||
|
||||
|
||||
<a id="#propagate_well_known_tags"></a>
|
||||
|
||||
## propagate_well_known_tags
|
||||
|
||||
<pre>
|
||||
propagate_well_known_tags(<a href="#propagate_well_known_tags-tags">tags</a>)
|
||||
</pre>
|
||||
|
||||
Returns a list of tags filtered from the input set that only contains the ones that are considered "well known"
|
||||
|
||||
These are listed in Bazel's documentation:
|
||||
https://docs.bazel.build/versions/main/test-encyclopedia.html#tag-conventions
|
||||
https://docs.bazel.build/versions/main/be/common-definitions.html#common-attributes
|
||||
|
||||
|
||||
**PARAMETERS**
|
||||
|
||||
|
||||
| Name | Description | Default Value |
|
||||
| :------------- | :------------- | :------------- |
|
||||
| <a id="propagate_well_known_tags-tags"></a>tags | List of tags to filter | <code>[]</code> |
|
||||
|
||||
**RETURNS**
|
||||
|
||||
List of tags that only contains the well known set
|
||||
|
||||
|
||||
<a id="#to_label"></a>
|
||||
|
||||
## to_label
|
||||
|
||||
<pre>
|
||||
to_label(<a href="#to_label-param">param</a>)
|
||||
</pre>
|
||||
|
||||
Converts a string to a Label. If Label is supplied, the same label is returned.
|
||||
|
||||
**PARAMETERS**
|
||||
|
||||
|
||||
| Name | Description | Default Value |
|
||||
| :------------- | :------------- | :------------- |
|
||||
| <a id="to_label-param"></a>param | a string representing a label or a Label | none |
|
||||
|
||||
**RETURNS**
|
||||
|
||||
a Label
|
||||
|
||||
|
|
@ -1,10 +1,7 @@
|
|||
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
|
||||
|
||||
# For stardoc to reference the files
|
||||
exports_files([
|
||||
"expand_make_vars.bzl",
|
||||
"params_file.bzl",
|
||||
])
|
||||
exports_files(glob(["*.bzl"]))
|
||||
|
||||
filegroup(
|
||||
name = "package_content",
|
||||
|
@ -30,3 +27,17 @@ bzl_library(
|
|||
visibility = ["//visibility:public"],
|
||||
deps = ["//lib/private:params_file"],
|
||||
)
|
||||
|
||||
bzl_library(
|
||||
name = "paths",
|
||||
srcs = ["paths.bzl"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//lib/private:paths"],
|
||||
)
|
||||
|
||||
bzl_library(
|
||||
name = "utils",
|
||||
srcs = ["utils.bzl"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//lib/private:utils"],
|
||||
)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
"Public API"
|
||||
|
||||
load("//lib/private:paths.bzl", "paths")
|
||||
|
||||
relative_file = paths.relative_file
|
|
@ -16,8 +16,21 @@ bzl_library(
|
|||
deps = [":expand_make_vars"],
|
||||
)
|
||||
|
||||
bzl_library(
|
||||
name = "paths",
|
||||
srcs = ["paths.bzl"],
|
||||
visibility = ["//lib:__subpackages__"],
|
||||
deps = ["@bazel_skylib//lib:paths"],
|
||||
)
|
||||
|
||||
bzl_library(
|
||||
name = "expand_make_vars",
|
||||
srcs = ["expand_make_vars.bzl"],
|
||||
visibility = ["//lib:__subpackages__"],
|
||||
)
|
||||
|
||||
bzl_library(
|
||||
name = "utils",
|
||||
srcs = ["utils.bzl"],
|
||||
visibility = ["//lib:__subpackages__"],
|
||||
)
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
"""Path utils built on top of Skylib's path utils"""
|
||||
|
||||
load("@bazel_skylib//lib:paths.bzl", _spaths = "paths")
|
||||
|
||||
def _relative_file(to_file, frm_file):
|
||||
"""Resolves a relative path between two files, "to_file" and "frm_file", they must share the same root
|
||||
|
||||
Args:
|
||||
to_file: the path with file name to resolve to, from frm
|
||||
frm_file: the path with file name to resolve from
|
||||
|
||||
Returns:
|
||||
The relative path from frm_file to to_file, including the file name
|
||||
"""
|
||||
|
||||
to_segments = _spaths.normalize(_spaths.join("/", to_file)).split("/")[:-1]
|
||||
frm_segments = _spaths.normalize(_spaths.join("/", frm_file)).split("/")[:-1]
|
||||
|
||||
if len(to_segments) == 0 and len(frm_segments) == 0:
|
||||
return to_file
|
||||
|
||||
if to_segments[0] != frm_segments[0]:
|
||||
fail("paths must share a common root, got '%s' and '%s'" % (to_file, frm_file))
|
||||
|
||||
longest_common = []
|
||||
for to_seg, frm_seg in zip(to_segments, frm_segments):
|
||||
if to_seg == frm_seg:
|
||||
longest_common.append(to_seg)
|
||||
else:
|
||||
break
|
||||
|
||||
split_point = len(longest_common)
|
||||
|
||||
if split_point == 0:
|
||||
fail("paths share no common ancestor, '%s' -> '%s'" % (frm_file, to_file))
|
||||
|
||||
return _spaths.join(
|
||||
*(
|
||||
[".."] * (len(frm_segments) - split_point) +
|
||||
to_segments[split_point:] +
|
||||
[_spaths.basename(to_file)]
|
||||
)
|
||||
)
|
||||
|
||||
paths = struct(
|
||||
relative_file = _relative_file,
|
||||
)
|
|
@ -0,0 +1,94 @@
|
|||
"""General utility functions"""
|
||||
|
||||
def _propagate_well_known_tags(tags = []):
|
||||
"""Returns a list of tags filtered from the input set that only contains the ones that are considered "well known"
|
||||
|
||||
These are listed in Bazel's documentation:
|
||||
https://docs.bazel.build/versions/main/test-encyclopedia.html#tag-conventions
|
||||
https://docs.bazel.build/versions/main/be/common-definitions.html#common-attributes
|
||||
|
||||
Args:
|
||||
tags: List of tags to filter
|
||||
|
||||
Returns:
|
||||
List of tags that only contains the well known set
|
||||
"""
|
||||
|
||||
WELL_KNOWN_TAGS = [
|
||||
"no-sandbox",
|
||||
"no-cache",
|
||||
"no-remote-cache",
|
||||
"no-remote-exec",
|
||||
"no-remote",
|
||||
"local",
|
||||
"requires-network",
|
||||
"block-network",
|
||||
"requires-fakeroot",
|
||||
"exclusive",
|
||||
"manual",
|
||||
"external",
|
||||
]
|
||||
|
||||
return [tag for tag in tags if tag in WELL_KNOWN_TAGS]
|
||||
|
||||
def _to_label(param):
|
||||
"""Converts a string to a Label. If Label is supplied, the same label is returned.
|
||||
|
||||
Args:
|
||||
param: a string representing a label or a Label
|
||||
|
||||
Returns:
|
||||
a Label
|
||||
"""
|
||||
param_type = type(param)
|
||||
if param_type == "string":
|
||||
if not param.startswith("@") and not param.startswith("//"):
|
||||
# resolve the relative label from the current package
|
||||
# if 'param' is in another workspace, then this would return the label relative to that workspace, eg:
|
||||
# Label("@my//foo:bar").relative("@other//baz:bill") == Label("@other//baz:bill")
|
||||
if param.startswith(":"):
|
||||
param = param[1:]
|
||||
if native.package_name():
|
||||
return Label("//" + native.package_name()).relative(param)
|
||||
else:
|
||||
return Label("//:" + param)
|
||||
return Label(param)
|
||||
elif param_type == "Label":
|
||||
return param
|
||||
else:
|
||||
fail("Expected 'string' or 'Label' but got '%s'" % param_type)
|
||||
|
||||
def _is_external_label(param):
|
||||
"""Returns True if the given Label (or stringy version of a label) represents a target outside of the workspace
|
||||
|
||||
Args:
|
||||
param: a string or label
|
||||
|
||||
Returns:
|
||||
a bool
|
||||
"""
|
||||
return len(_to_label(param).workspace_root) > 0
|
||||
|
||||
# Path to the root of the workspace
|
||||
def _path_to_workspace_root():
|
||||
""" Retuns the path to the workspace root under bazel
|
||||
|
||||
Returns:
|
||||
Path to the workspace root
|
||||
"""
|
||||
return "/".join([".."] * len(native.package_name().split("/")))
|
||||
|
||||
# Like glob() but returns directories only
|
||||
def _glob_directories(include, **kwargs):
|
||||
all = native.glob(include, exclude_directories = 0, **kwargs)
|
||||
files = native.glob(include, **kwargs)
|
||||
directories = [p for p in all if p not in files]
|
||||
return directories
|
||||
|
||||
utils = struct(
|
||||
is_external_label = _is_external_label,
|
||||
glob_directories = _glob_directories,
|
||||
path_to_workspace_root = _path_to_workspace_root,
|
||||
propagate_well_known_tags = _propagate_well_known_tags,
|
||||
to_label = _to_label,
|
||||
)
|
|
@ -1,3 +1,9 @@
|
|||
load(":expand_make_vars_test.bzl", "expand_make_vars_test_suite")
|
||||
load(":utils_test.bzl", "utils_test_suite")
|
||||
load(":paths_test.bzl", "paths_test_suite")
|
||||
|
||||
expand_make_vars_test_suite(name = "expand_make_vars_test")
|
||||
expand_make_vars_test_suite()
|
||||
|
||||
paths_test_suite()
|
||||
|
||||
utils_test_suite()
|
||||
|
|
|
@ -25,5 +25,5 @@ def _variables_test_impl(ctx):
|
|||
# but their names are arbitrary and don't appear anywhere.
|
||||
t0_test = unittest.make(_variables_test_impl)
|
||||
|
||||
def expand_make_vars_test_suite(name):
|
||||
unittest.suite(name, t0_test)
|
||||
def expand_make_vars_test_suite():
|
||||
unittest.suite("make_vars_tests", t0_test)
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
"""unit tests for paths"""
|
||||
|
||||
load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest")
|
||||
load("//lib/private:paths.bzl", "paths")
|
||||
|
||||
def _relative_file_test_impl(ctx):
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
asserts.equals(
|
||||
env,
|
||||
"lib/requirements.in",
|
||||
paths.relative_file(
|
||||
"bazel/python/internal/pip/test/lib/requirements.in",
|
||||
"bazel/python/internal/pip/test/requirements.out",
|
||||
),
|
||||
)
|
||||
|
||||
asserts.equals(
|
||||
env,
|
||||
"pip/test/lib/requirements.in",
|
||||
paths.relative_file(
|
||||
"bazel/python/internal/pip/test/lib/requirements.in",
|
||||
"bazel/python/internal/requirements.out",
|
||||
),
|
||||
)
|
||||
|
||||
asserts.equals(
|
||||
env,
|
||||
"bazel/python/internal/pip/test/lib/requirements.in",
|
||||
paths.relative_file(
|
||||
"/bazel/python/internal/pip/test/lib/requirements.in",
|
||||
"/requirements.out",
|
||||
),
|
||||
)
|
||||
|
||||
asserts.equals(
|
||||
env,
|
||||
"../requirements.in",
|
||||
paths.relative_file(
|
||||
"bazel/python/internal/pip/test/requirements.in",
|
||||
"bazel/python/internal/pip/test/lib/requirements.in",
|
||||
),
|
||||
)
|
||||
|
||||
asserts.equals(
|
||||
env,
|
||||
"../requirements.out",
|
||||
paths.relative_file(
|
||||
"bazel/python/internal/pip/test/requirements.out",
|
||||
"bazel/python/internal/pip/test/lib/requirements.in",
|
||||
),
|
||||
)
|
||||
|
||||
asserts.equals(
|
||||
env,
|
||||
"requirements.out",
|
||||
paths.relative_file(
|
||||
"bazel/python/internal/pip/example/service/requirements.out",
|
||||
"bazel/python/internal/pip/example/service/requirements.in",
|
||||
),
|
||||
)
|
||||
|
||||
asserts.equals(
|
||||
env,
|
||||
"../service/requirements.out",
|
||||
paths.relative_file(
|
||||
"bazel/python/internal/pip/example/service/requirements.out",
|
||||
"bazel/python/internal/pip/example/lib/requirements.in",
|
||||
),
|
||||
)
|
||||
|
||||
asserts.equals(
|
||||
env,
|
||||
"../lib/bar/requirements.in",
|
||||
paths.relative_file(
|
||||
"bazel/python/internal/pip/example/lib/bar/requirements.in",
|
||||
"bazel/python/internal/pip/example/service/requirements.out",
|
||||
),
|
||||
)
|
||||
|
||||
asserts.equals(
|
||||
env,
|
||||
"../../../example/lib/bar/requirements.in",
|
||||
paths.relative_file(
|
||||
"bazel/python/internal/pip/example/lib/bar/requirements.in",
|
||||
"bazel/python/internal/pip/lib/example/service/requirements.out",
|
||||
),
|
||||
)
|
||||
|
||||
asserts.equals(
|
||||
env,
|
||||
"requirements.in",
|
||||
paths.relative_file(
|
||||
"requirements.in",
|
||||
"requirements.out",
|
||||
),
|
||||
)
|
||||
|
||||
asserts.equals(
|
||||
env,
|
||||
"requirements.in",
|
||||
paths.relative_file(
|
||||
"/bazel/requirements.in",
|
||||
"/bazel/requirements.out",
|
||||
),
|
||||
)
|
||||
|
||||
asserts.equals(
|
||||
env,
|
||||
"../requirements.in",
|
||||
paths.relative_file(
|
||||
"/requirements.in",
|
||||
"/bazel/requirements.out",
|
||||
),
|
||||
)
|
||||
|
||||
return unittest.end(env)
|
||||
|
||||
relative_file_test = unittest.make(_relative_file_test_impl)
|
||||
|
||||
def paths_test_suite():
|
||||
unittest.suite("relative_file_tests", relative_file_test)
|
|
@ -0,0 +1,76 @@
|
|||
"Unit tests for test.bzl"
|
||||
|
||||
load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest")
|
||||
load("//lib/private:utils.bzl", "utils")
|
||||
|
||||
def _to_label_test_impl(ctx):
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
# assert label/string comparisons work as expected
|
||||
asserts.true(env, "//some/label" != Label("//some/label"))
|
||||
asserts.true(env, "//some/label" != Label("//some/other/label"))
|
||||
asserts.true(env, Label("//some/label") == Label("//some/label"))
|
||||
asserts.true(env, "//some/label" != Label("//some/label"))
|
||||
asserts.true(env, "//some/label" != Label("//some/other/label"))
|
||||
|
||||
# assert that to_label can convert from string to label
|
||||
asserts.true(env, utils.to_label("//hello/world") == Label("//hello/world:world"))
|
||||
asserts.true(env, utils.to_label("//hello/world:world") == Label("//hello/world:world"))
|
||||
|
||||
# assert that to_label will handle a Label as an argument
|
||||
asserts.true(env, utils.to_label(Label("//hello/world")) == Label("//hello/world:world"))
|
||||
asserts.true(env, utils.to_label(Label("//hello/world:world")) == Label("//hello/world:world"))
|
||||
|
||||
# relative labels
|
||||
for (actual, expected) in ctx.attr.relative_asserts.items():
|
||||
asserts.true(env, actual.label == Label(expected))
|
||||
|
||||
return unittest.end(env)
|
||||
|
||||
def _is_external_label_test_impl(ctx):
|
||||
env = unittest.begin(ctx)
|
||||
|
||||
# assert that labels and strings that are constructed within this workspace return false
|
||||
asserts.false(env, utils.is_external_label("//some/label"))
|
||||
asserts.false(env, utils.is_external_label(Label("//some/label")))
|
||||
asserts.false(env, utils.is_external_label(Label("@aspect_bazel_lib//some/label")))
|
||||
asserts.false(env, ctx.attr.internal_with_workspace_as_string)
|
||||
|
||||
# assert that labels and string that give a workspace return true
|
||||
asserts.true(env, utils.is_external_label(Label("@foo//some/label")))
|
||||
asserts.true(env, ctx.attr.external_as_string)
|
||||
|
||||
return unittest.end(env)
|
||||
|
||||
to_label_test = unittest.make(
|
||||
_to_label_test_impl,
|
||||
attrs = {
|
||||
"relative_asserts": attr.label_keyed_string_dict(
|
||||
allow_files = True,
|
||||
mandatory = True,
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
is_external_label_test = unittest.make(
|
||||
_is_external_label_test_impl,
|
||||
attrs = {
|
||||
"external_as_string": attr.bool(
|
||||
mandatory = True,
|
||||
),
|
||||
"internal_with_workspace_as_string": attr.bool(
|
||||
mandatory = True,
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
def utils_test_suite():
|
||||
to_label_test(name = "to_label_tests", relative_asserts = {
|
||||
utils.to_label(":utils_test.bzl"): "//lib/tests:utils_test.bzl",
|
||||
})
|
||||
|
||||
is_external_label_test(
|
||||
name = "is_external_label_tests",
|
||||
external_as_string = utils.is_external_label("@foo//some/label"),
|
||||
internal_with_workspace_as_string = utils.is_external_label("@aspect_bazel_lib//some/label"),
|
||||
)
|
|
@ -0,0 +1,9 @@
|
|||
"Public API"
|
||||
|
||||
load("//lib/private:utils.bzl", "utils")
|
||||
|
||||
is_external_label = utils.is_external_label
|
||||
glob_directories = utils.glob_directories
|
||||
path_to_workspace_root = utils.path_to_workspace_root
|
||||
propagate_well_known_tags = utils.propagate_well_known_tags
|
||||
to_label = utils.to_label
|
Loading…
Reference in New Issue