Copy rules_directory to bazel-skylib. (#510)
Original implementation is at https://github.com/matts1/rules_directory
This commit is contained in:
parent
b459822483
commit
a464f69faa
|
@ -16,6 +16,7 @@ matrix:
|
||||||
.reusable_targets: &reusable_targets
|
.reusable_targets: &reusable_targets
|
||||||
? "--"
|
? "--"
|
||||||
? "//..."
|
? "//..."
|
||||||
|
? "@external_directory_tests//..."
|
||||||
? "@bazel_skylib_gazelle_plugin//..."
|
? "@bazel_skylib_gazelle_plugin//..."
|
||||||
|
|
||||||
.reusable_config: &reusable_config
|
.reusable_config: &reusable_config
|
||||||
|
|
|
@ -18,6 +18,7 @@ bazel_dep(name = "rules_license", version = "0.0.7")
|
||||||
# Build-only / test-only dependencies
|
# Build-only / test-only dependencies
|
||||||
bazel_dep(name = "stardoc", version = "0.6.2", dev_dependency = True, repo_name = "io_bazel_stardoc")
|
bazel_dep(name = "stardoc", version = "0.6.2", dev_dependency = True, repo_name = "io_bazel_stardoc")
|
||||||
bazel_dep(name = "rules_pkg", version = "0.9.1", dev_dependency = True)
|
bazel_dep(name = "rules_pkg", version = "0.9.1", dev_dependency = True)
|
||||||
|
bazel_dep(name = "rules_testing", version = "0.6.0", dev_dependency = True)
|
||||||
|
|
||||||
# Needed for bazelci and for building distribution tarballs.
|
# Needed for bazelci and for building distribution tarballs.
|
||||||
# If using an unreleased version of bazel_skylib via git_override, apply
|
# If using an unreleased version of bazel_skylib via git_override, apply
|
||||||
|
@ -28,8 +29,11 @@ local_path_override(
|
||||||
path = "gazelle",
|
path = "gazelle",
|
||||||
)
|
)
|
||||||
|
|
||||||
as_extension_test_ext = use_extension("//tests:modules_test.bzl", "as_extension_test_ext")
|
external_directory_tests_ext = use_extension("//tests/directory:external_directory_tests.bzl", "external_directory_tests_ext", dev_dependency = True)
|
||||||
|
use_repo(external_directory_tests_ext, "external_directory_tests")
|
||||||
|
|
||||||
|
as_extension_test_ext = use_extension("//tests:modules_test.bzl", "as_extension_test_ext", dev_dependency = True)
|
||||||
use_repo(as_extension_test_ext, "bar", "foo")
|
use_repo(as_extension_test_ext, "bar", "foo")
|
||||||
|
|
||||||
use_all_repos_test_ext = use_extension("//tests:modules_test.bzl", "use_all_repos_test_ext")
|
use_all_repos_test_ext = use_extension("//tests:modules_test.bzl", "use_all_repos_test_ext", dev_dependency = True)
|
||||||
use_repo(use_all_repos_test_ext, "baz", "qux")
|
use_repo(use_all_repos_test_ext, "baz", "qux")
|
||||||
|
|
|
@ -62,6 +62,9 @@ s = shell.quote(p)
|
||||||
* [analysis_test](docs/analysis_test_doc.md)
|
* [analysis_test](docs/analysis_test_doc.md)
|
||||||
* [build_test](docs/build_test_doc.md)
|
* [build_test](docs/build_test_doc.md)
|
||||||
* [common_settings](docs/common_settings_doc.md)
|
* [common_settings](docs/common_settings_doc.md)
|
||||||
|
* [directories](docs/copy_directory_doc.md)
|
||||||
|
* [directory](docs/directory_doc.md)
|
||||||
|
* [subdirectory](docs/subdirectory_doc.md)
|
||||||
* [copy_directory](docs/copy_directory_doc.md)
|
* [copy_directory](docs/copy_directory_doc.md)
|
||||||
* [copy_file](docs/copy_file_doc.md)
|
* [copy_file](docs/copy_file_doc.md)
|
||||||
* [diff_test](docs/diff_test_doc.md)
|
* [diff_test](docs/diff_test_doc.md)
|
||||||
|
|
10
WORKSPACE
10
WORKSPACE
|
@ -68,6 +68,16 @@ load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies")
|
||||||
|
|
||||||
rules_pkg_dependencies()
|
rules_pkg_dependencies()
|
||||||
|
|
||||||
|
http_archive(
|
||||||
|
name = "rules_testing",
|
||||||
|
sha256 = "02c62574631876a4e3b02a1820cb51167bb9cdcdea2381b2fa9d9b8b11c407c4",
|
||||||
|
strip_prefix = "rules_testing-0.6.0",
|
||||||
|
url = "https://github.com/bazelbuild/rules_testing/releases/download/v0.6.0/rules_testing-v0.6.0.tar.gz",
|
||||||
|
)
|
||||||
|
|
||||||
load("//lib:unittest.bzl", "register_unittest_toolchains")
|
load("//lib:unittest.bzl", "register_unittest_toolchains")
|
||||||
|
load("//tests/directory:external_directory_tests.bzl", "external_directory_tests")
|
||||||
|
|
||||||
|
external_directory_tests(name = "external_directory_tests")
|
||||||
|
|
||||||
register_unittest_toolchains()
|
register_unittest_toolchains()
|
||||||
|
|
18
docs/BUILD
18
docs/BUILD
|
@ -58,6 +58,24 @@ stardoc_with_diff_test(
|
||||||
out_label = "//docs:diff_test_doc.md",
|
out_label = "//docs:diff_test_doc.md",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
stardoc_with_diff_test(
|
||||||
|
name = "directory",
|
||||||
|
bzl_library_target = "//rules/directory:directory",
|
||||||
|
out_label = "//docs:directory_doc.md",
|
||||||
|
)
|
||||||
|
|
||||||
|
stardoc_with_diff_test(
|
||||||
|
name = "directory_providers",
|
||||||
|
bzl_library_target = "//rules/directory:providers",
|
||||||
|
out_label = "//docs:directory_providers_doc.md",
|
||||||
|
)
|
||||||
|
|
||||||
|
stardoc_with_diff_test(
|
||||||
|
name = "directory_subdirectory",
|
||||||
|
bzl_library_target = "//rules/directory:subdirectory",
|
||||||
|
out_label = "//docs:directory_subdirectory_doc.md",
|
||||||
|
)
|
||||||
|
|
||||||
stardoc_with_diff_test(
|
stardoc_with_diff_test(
|
||||||
name = "expand_template",
|
name = "expand_template",
|
||||||
bzl_library_target = "//rules:expand_template",
|
bzl_library_target = "//rules:expand_template",
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
<!-- Generated with Stardoc: http://skydoc.bazel.build -->
|
||||||
|
|
||||||
|
Skylib module containing rules to create metadata about directories.
|
||||||
|
|
||||||
|
<a id="directory"></a>
|
||||||
|
|
||||||
|
## directory
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
directory(<a href="#directory-name">name</a>, <a href="#directory-srcs">srcs</a>)
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**ATTRIBUTES**
|
||||||
|
|
||||||
|
|
||||||
|
| Name | Description | Type | Mandatory | Default |
|
||||||
|
| :------------- | :------------- | :------------- | :------------- | :------------- |
|
||||||
|
| <a id="directory-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
|
||||||
|
| <a id="directory-srcs"></a>srcs | - | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
<!-- Generated with Stardoc: http://skydoc.bazel.build -->
|
||||||
|
|
||||||
|
Skylib module containing providers for directories.
|
||||||
|
|
||||||
|
<a id="DirectoryInfo"></a>
|
||||||
|
|
||||||
|
## DirectoryInfo
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
DirectoryInfo(<a href="#DirectoryInfo-entries">entries</a>, <a href="#DirectoryInfo-transitive_files">transitive_files</a>, <a href="#DirectoryInfo-path">path</a>, <a href="#DirectoryInfo-human_readable">human_readable</a>, <a href="#DirectoryInfo-get_path">get_path</a>, <a href="#DirectoryInfo-get_file">get_file</a>, <a href="#DirectoryInfo-get_subdirectory">get_subdirectory</a>)
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
Information about a directory
|
||||||
|
|
||||||
|
**FIELDS**
|
||||||
|
|
||||||
|
|
||||||
|
| Name | Description |
|
||||||
|
| :------------- | :------------- |
|
||||||
|
| <a id="DirectoryInfo-entries"></a>entries | (Dict[str, Either[File, DirectoryInfo]]) The entries contained directly within. Ordered by filename |
|
||||||
|
| <a id="DirectoryInfo-transitive_files"></a>transitive_files | (depset[File]) All files transitively contained within this directory. |
|
||||||
|
| <a id="DirectoryInfo-path"></a>path | (string) Path to all files contained within this directory. |
|
||||||
|
| <a id="DirectoryInfo-human_readable"></a>human_readable | (string) A human readable identifier for a directory. Useful for providing error messages to a user. |
|
||||||
|
| <a id="DirectoryInfo-get_path"></a>get_path | (Function(str) -> DirectoryInfo\|File) A function to return the entry corresponding to the joined path. |
|
||||||
|
| <a id="DirectoryInfo-get_file"></a>get_file | (Function(str) -> File) A function to return the entry corresponding to the joined path. |
|
||||||
|
| <a id="DirectoryInfo-get_subdirectory"></a>get_subdirectory | (Function(str) -> DirectoryInfo) A function to return the entry corresponding to the joined path. |
|
||||||
|
|
||||||
|
|
||||||
|
<a id="create_directory_info"></a>
|
||||||
|
|
||||||
|
## create_directory_info
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
create_directory_info(<a href="#create_directory_info-kwargs">kwargs</a>)
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**PARAMETERS**
|
||||||
|
|
||||||
|
|
||||||
|
| Name | Description | Default Value |
|
||||||
|
| :------------- | :------------- | :------------- |
|
||||||
|
| <a id="create_directory_info-kwargs"></a>kwargs | <p align="center"> - </p> | none |
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
<!-- Generated with Stardoc: http://skydoc.bazel.build -->
|
||||||
|
|
||||||
|
Skylib module containing rules to create metadata about subdirectories.
|
||||||
|
|
||||||
|
<a id="subdirectory"></a>
|
||||||
|
|
||||||
|
## subdirectory
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
subdirectory(<a href="#subdirectory-name">name</a>, <a href="#subdirectory-parent">parent</a>, <a href="#subdirectory-path">path</a>)
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**ATTRIBUTES**
|
||||||
|
|
||||||
|
|
||||||
|
| Name | Description | Type | Mandatory | Default |
|
||||||
|
| :------------- | :------------- | :------------- | :------------- | :------------- |
|
||||||
|
| <a id="subdirectory-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
|
||||||
|
| <a id="subdirectory-parent"></a>parent | A label corresponding to the parent directory (or subdirectory). | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
|
||||||
|
| <a id="subdirectory-path"></a>path | A path within the parent directory (eg. "path/to/subdir") | String | required | |
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
<!-- Generated with Stardoc: http://skydoc.bazel.build -->
|
||||||
|
|
||||||
|
Skylib module containing utility functions related to directories.
|
||||||
|
|
||||||
|
<a id="get_child"></a>
|
||||||
|
|
||||||
|
## get_child
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
get_child(<a href="#get_child-directory">directory</a>, <a href="#get_child-name">name</a>, <a href="#get_child-require_dir">require_dir</a>, <a href="#get_child-require_file">require_file</a>)
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
Gets the direct child of a directory.
|
||||||
|
|
||||||
|
**PARAMETERS**
|
||||||
|
|
||||||
|
|
||||||
|
| Name | Description | Default Value |
|
||||||
|
| :------------- | :------------- | :------------- |
|
||||||
|
| <a id="get_child-directory"></a>directory | (DirectoryInfo) The directory to look within. | none |
|
||||||
|
| <a id="get_child-name"></a>name | (string) The name of the directory/file to look for. | none |
|
||||||
|
| <a id="get_child-require_dir"></a>require_dir | (bool) If true, throws an error if the value is not a directory. | `False` |
|
||||||
|
| <a id="get_child-require_file"></a>require_file | (bool) If true, throws an error if the value is not a file. | `False` |
|
||||||
|
|
||||||
|
**RETURNS**
|
||||||
|
|
||||||
|
(File|DirectoryInfo) The content contained within.
|
||||||
|
|
||||||
|
|
||||||
|
<a id="get_relative"></a>
|
||||||
|
|
||||||
|
## get_relative
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
get_relative(<a href="#get_relative-directory">directory</a>, <a href="#get_relative-path">path</a>, <a href="#get_relative-require_dir">require_dir</a>, <a href="#get_relative-require_file">require_file</a>)
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
Gets a subdirectory contained within a tree of another directory.
|
||||||
|
|
||||||
|
**PARAMETERS**
|
||||||
|
|
||||||
|
|
||||||
|
| Name | Description | Default Value |
|
||||||
|
| :------------- | :------------- | :------------- |
|
||||||
|
| <a id="get_relative-directory"></a>directory | (DirectoryInfo) The directory to look within. | none |
|
||||||
|
| <a id="get_relative-path"></a>path | (string) The path of the directory to look for within it. | none |
|
||||||
|
| <a id="get_relative-require_dir"></a>require_dir | (bool) If true, throws an error if the value is not a directory. | `False` |
|
||||||
|
| <a id="get_relative-require_file"></a>require_file | (bool) If true, throws an error if the value is not a file. | `False` |
|
||||||
|
|
||||||
|
**RETURNS**
|
||||||
|
|
||||||
|
(File|DirectoryInfo) The directory contained within.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
|
||||||
|
|
||||||
|
licenses(["notice"])
|
||||||
|
|
||||||
|
# export bzl files for the documentation
|
||||||
|
exports_files(
|
||||||
|
glob(["*.bzl"]),
|
||||||
|
visibility = ["//:__subpackages__"],
|
||||||
|
)
|
||||||
|
|
||||||
|
bzl_library(
|
||||||
|
name = "directory",
|
||||||
|
srcs = ["directory.bzl"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
":providers",
|
||||||
|
"//lib:paths",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
bzl_library(
|
||||||
|
name = "providers",
|
||||||
|
srcs = ["providers.bzl"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//rules/directory/private:paths",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
bzl_library(
|
||||||
|
name = "subdirectory",
|
||||||
|
srcs = ["subdirectory.bzl"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
":providers",
|
||||||
|
],
|
||||||
|
)
|
|
@ -0,0 +1,140 @@
|
||||||
|
# Copyright 2024 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 rules to create metadata about directories."""
|
||||||
|
|
||||||
|
load("//lib:paths.bzl", "paths")
|
||||||
|
load(":providers.bzl", "DirectoryInfo", "create_directory_info")
|
||||||
|
|
||||||
|
def _prefix_match(f, prefixes):
|
||||||
|
for prefix in prefixes:
|
||||||
|
if f.path.startswith(prefix):
|
||||||
|
return prefix
|
||||||
|
fail("Expected {path} to start with one of {prefixes}".format(path = f.path, prefixes = list(prefixes)))
|
||||||
|
|
||||||
|
def _choose_path(prefixes):
|
||||||
|
filtered = {prefix: example for prefix, example in prefixes.items() if example}
|
||||||
|
if len(filtered) > 1:
|
||||||
|
examples = list(filtered.values())
|
||||||
|
fail(
|
||||||
|
"Your sources contain {} and {}.\n\n".format(
|
||||||
|
examples[0],
|
||||||
|
examples[1],
|
||||||
|
) +
|
||||||
|
"Having both source and generated files in a single directory is " +
|
||||||
|
"unsupported, since they will appear in two different " +
|
||||||
|
"directories in the bazel execroot. You may want to consider " +
|
||||||
|
"splitting your directory into one for source files and one for " +
|
||||||
|
"generated files.",
|
||||||
|
)
|
||||||
|
|
||||||
|
# If there's no entries, use the source path (it's always first in the dict)
|
||||||
|
return list(filtered if filtered else prefixes)[0][:-1]
|
||||||
|
|
||||||
|
def _directory_impl(ctx):
|
||||||
|
# Declare a generated file so that we can get the path to generated files.
|
||||||
|
f = ctx.actions.declare_file("_directory_rule_" + ctx.label.name)
|
||||||
|
ctx.actions.write(f, "")
|
||||||
|
|
||||||
|
source_prefix = ctx.label.package + "/"
|
||||||
|
if ctx.label.workspace_root:
|
||||||
|
source_prefix = ctx.label.workspace_root + "/" + source_prefix
|
||||||
|
|
||||||
|
# Mapping of a prefix to an arbitrary (but deterministic) file matching that path.
|
||||||
|
# The arbitrary file is used to present error messages if we have both generated files and source files.
|
||||||
|
prefixes = {
|
||||||
|
source_prefix: None,
|
||||||
|
f.dirname + "/": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
root_metadata = struct(
|
||||||
|
directories = {},
|
||||||
|
files = [],
|
||||||
|
relative = "",
|
||||||
|
human_readable = str(ctx.label),
|
||||||
|
)
|
||||||
|
|
||||||
|
topological = [root_metadata]
|
||||||
|
for src in ctx.files.srcs:
|
||||||
|
prefix = _prefix_match(src, prefixes)
|
||||||
|
prefixes[prefix] = src
|
||||||
|
relative = src.path[len(prefix):].split("/")
|
||||||
|
current_path = root_metadata
|
||||||
|
for dirname in relative[:-1]:
|
||||||
|
if dirname not in current_path.directories:
|
||||||
|
dir_metadata = struct(
|
||||||
|
directories = {},
|
||||||
|
files = [],
|
||||||
|
relative = paths.join(current_path.relative, dirname),
|
||||||
|
human_readable = paths.join(current_path.human_readable, dirname),
|
||||||
|
)
|
||||||
|
current_path.directories[dirname] = dir_metadata
|
||||||
|
topological.append(dir_metadata)
|
||||||
|
|
||||||
|
current_path = current_path.directories[dirname]
|
||||||
|
|
||||||
|
current_path.files.append(src)
|
||||||
|
|
||||||
|
# The output DirectoryInfos. Key them by something arbitrary but unique.
|
||||||
|
# In this case, we choose relative.
|
||||||
|
out = {}
|
||||||
|
|
||||||
|
root_path = _choose_path(prefixes)
|
||||||
|
|
||||||
|
# By doing it in reversed topological order, we ensure that a child is
|
||||||
|
# created before its parents. This means that when we create a provider,
|
||||||
|
# we can always guarantee that a depset of its children will work.
|
||||||
|
for dir_metadata in reversed(topological):
|
||||||
|
directories = {
|
||||||
|
dirname: out[subdir_metadata.relative]
|
||||||
|
for dirname, subdir_metadata in sorted(dir_metadata.directories.items())
|
||||||
|
}
|
||||||
|
entries = {
|
||||||
|
file.basename: file
|
||||||
|
for file in dir_metadata.files
|
||||||
|
}
|
||||||
|
entries.update(directories)
|
||||||
|
|
||||||
|
transitive_files = depset(
|
||||||
|
direct = sorted(dir_metadata.files, key = lambda f: f.basename),
|
||||||
|
transitive = [
|
||||||
|
d.transitive_files
|
||||||
|
for d in directories.values()
|
||||||
|
],
|
||||||
|
order = "preorder",
|
||||||
|
)
|
||||||
|
directory = create_directory_info(
|
||||||
|
entries = {k: v for k, v in sorted(entries.items())},
|
||||||
|
transitive_files = transitive_files,
|
||||||
|
path = paths.join(root_path, dir_metadata.relative) if dir_metadata.relative else root_path,
|
||||||
|
human_readable = dir_metadata.human_readable,
|
||||||
|
)
|
||||||
|
out[dir_metadata.relative] = directory
|
||||||
|
|
||||||
|
root_directory = out[root_metadata.relative]
|
||||||
|
|
||||||
|
return [
|
||||||
|
root_directory,
|
||||||
|
DefaultInfo(files = root_directory.transitive_files),
|
||||||
|
]
|
||||||
|
|
||||||
|
directory = rule(
|
||||||
|
implementation = _directory_impl,
|
||||||
|
attrs = {
|
||||||
|
"srcs": attr.label_list(
|
||||||
|
allow_files = True,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
provides = [DirectoryInfo],
|
||||||
|
)
|
|
@ -0,0 +1,18 @@
|
||||||
|
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
|
||||||
|
|
||||||
|
licenses(["notice"])
|
||||||
|
|
||||||
|
# export bzl files for the documentation
|
||||||
|
exports_files(
|
||||||
|
glob(["*.bzl"]),
|
||||||
|
visibility = ["//:__subpackages__"],
|
||||||
|
)
|
||||||
|
|
||||||
|
bzl_library(
|
||||||
|
name = "paths",
|
||||||
|
srcs = ["paths.bzl"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//lib:paths",
|
||||||
|
],
|
||||||
|
)
|
|
@ -0,0 +1,94 @@
|
||||||
|
# Copyright 2024 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 path operations on directories."""
|
||||||
|
|
||||||
|
load("//lib:paths.bzl", "paths")
|
||||||
|
|
||||||
|
_NOT_FOUND = """{directory} does not contain an entry named {name}.
|
||||||
|
Instead, it contains the following entries:
|
||||||
|
{children}
|
||||||
|
|
||||||
|
"""
|
||||||
|
_WRONG_TYPE = "Expected {dir}/{name} to have type {want}, but got {got}"
|
||||||
|
|
||||||
|
# These correspond to an "enum".
|
||||||
|
FILE = "file"
|
||||||
|
DIRECTORY = "directory"
|
||||||
|
|
||||||
|
def _check_path_relative(path):
|
||||||
|
if paths.is_absolute(path):
|
||||||
|
fail("Path must be relative. Got {path}".format(path = path))
|
||||||
|
|
||||||
|
def _get_direct_child(directory, name, require_type = None):
|
||||||
|
"""Gets the direct child of a directory.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
directory: (DirectoryInfo) The directory to look within.
|
||||||
|
name: (string) The name of the directory/file to look for.
|
||||||
|
require_type: (Optional[DIRECTORY|FILE]) If provided, must return
|
||||||
|
either a the corresponding type.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(File|DirectoryInfo) The content contained within.
|
||||||
|
"""
|
||||||
|
entry = directory.entries.get(name, None)
|
||||||
|
if entry == None:
|
||||||
|
fail(_NOT_FOUND.format(
|
||||||
|
directory = directory.human_readable,
|
||||||
|
name = repr(name),
|
||||||
|
children = "\n".join(directory.entries.keys()),
|
||||||
|
))
|
||||||
|
if require_type == DIRECTORY and type(entry) == "File":
|
||||||
|
fail(_WRONG_TYPE.format(
|
||||||
|
dir = directory.human_readable,
|
||||||
|
name = name,
|
||||||
|
want = "Directory",
|
||||||
|
got = "File",
|
||||||
|
))
|
||||||
|
|
||||||
|
if require_type == FILE and type(entry) != "File":
|
||||||
|
fail(_WRONG_TYPE.format(
|
||||||
|
dir = directory.human_readable,
|
||||||
|
name = name,
|
||||||
|
want = "File",
|
||||||
|
got = "Directory",
|
||||||
|
))
|
||||||
|
return entry
|
||||||
|
|
||||||
|
def get_path(directory, path, require_type = None):
|
||||||
|
"""Gets a subdirectory or file contained within a directory.
|
||||||
|
|
||||||
|
Example: `get_path(directory, "a/b", require_type=FILE)`
|
||||||
|
-> the file corresponding to `directory.path + "/a/b"`
|
||||||
|
|
||||||
|
Args:
|
||||||
|
directory: (DirectoryInfo) The directory to look within.
|
||||||
|
path: (string) The path of the directory to look for within it.
|
||||||
|
require_type: (Optional[DIRECTORY|FILE]) If provided, must return
|
||||||
|
either a the corresponding type.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(File|DirectoryInfo) The directory contained within.
|
||||||
|
"""
|
||||||
|
_check_path_relative(path)
|
||||||
|
|
||||||
|
chunks = path.split("/")
|
||||||
|
for dirname in chunks[:-1]:
|
||||||
|
directory = _get_direct_child(directory, dirname, require_type = DIRECTORY)
|
||||||
|
return _get_direct_child(
|
||||||
|
directory,
|
||||||
|
chunks[-1],
|
||||||
|
require_type = require_type,
|
||||||
|
)
|
|
@ -0,0 +1,46 @@
|
||||||
|
# Copyright 2024 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 providers for directories."""
|
||||||
|
|
||||||
|
load("//rules/directory/private:paths.bzl", "DIRECTORY", "FILE", "get_path")
|
||||||
|
|
||||||
|
def _init_directory_info(**kwargs):
|
||||||
|
self = struct(**kwargs)
|
||||||
|
kwargs.update(
|
||||||
|
get_path = lambda path: get_path(self, path, require_type = None),
|
||||||
|
get_file = lambda path: get_path(self, path, require_type = FILE),
|
||||||
|
get_subdirectory = lambda path: get_path(self, path, require_type = DIRECTORY),
|
||||||
|
)
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
# TODO: Once bazel 5 no longer needs to be supported, remove this function, and add
|
||||||
|
# init = _init_directory_info to the provider below
|
||||||
|
# buildifier: disable=function-docstring
|
||||||
|
def create_directory_info(**kwargs):
|
||||||
|
return DirectoryInfo(**_init_directory_info(**kwargs))
|
||||||
|
|
||||||
|
DirectoryInfo = provider(
|
||||||
|
doc = "Information about a directory",
|
||||||
|
# @unsorted-dict-items
|
||||||
|
fields = {
|
||||||
|
"entries": "(Dict[str, Either[File, DirectoryInfo]]) The entries contained directly within. Ordered by filename",
|
||||||
|
"transitive_files": "(depset[File]) All files transitively contained within this directory.",
|
||||||
|
"path": "(string) Path to all files contained within this directory.",
|
||||||
|
"human_readable": "(string) A human readable identifier for a directory. Useful for providing error messages to a user.",
|
||||||
|
"get_path": "(Function(str) -> DirectoryInfo|File) A function to return the entry corresponding to the joined path.",
|
||||||
|
"get_file": "(Function(str) -> File) A function to return the entry corresponding to the joined path.",
|
||||||
|
"get_subdirectory": "(Function(str) -> DirectoryInfo) A function to return the entry corresponding to the joined path.",
|
||||||
|
},
|
||||||
|
)
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Copyright 2024 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 rules to create metadata about subdirectories."""
|
||||||
|
|
||||||
|
load(":providers.bzl", "DirectoryInfo")
|
||||||
|
|
||||||
|
def _subdirectory_impl(ctx):
|
||||||
|
dir = ctx.attr.parent[DirectoryInfo].get_subdirectory(ctx.attr.path)
|
||||||
|
return [
|
||||||
|
dir,
|
||||||
|
DefaultInfo(files = dir.transitive_files),
|
||||||
|
]
|
||||||
|
|
||||||
|
subdirectory = rule(
|
||||||
|
implementation = _subdirectory_impl,
|
||||||
|
attrs = {
|
||||||
|
"parent": attr.label(
|
||||||
|
providers = [DirectoryInfo],
|
||||||
|
mandatory = True,
|
||||||
|
doc = "A label corresponding to the parent directory (or subdirectory).",
|
||||||
|
),
|
||||||
|
"path": attr.string(
|
||||||
|
mandatory = True,
|
||||||
|
doc = "A path within the parent directory (eg. \"path/to/subdir\")",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
provides = [DirectoryInfo],
|
||||||
|
)
|
|
@ -0,0 +1,33 @@
|
||||||
|
load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
|
||||||
|
load("@bazel_skylib//rules/directory:directory.bzl", "directory")
|
||||||
|
load(":directory_test.bzl", "directory_test_suite")
|
||||||
|
load(":subdirectory_test.bzl", "subdirectory_test_suite")
|
||||||
|
|
||||||
|
directory(
|
||||||
|
name = "root",
|
||||||
|
srcs = glob(["testdata/**"]),
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "f1_filegroup",
|
||||||
|
srcs = ["testdata/f1"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "f2_filegroup",
|
||||||
|
srcs = ["testdata/subdir/f2"],
|
||||||
|
)
|
||||||
|
|
||||||
|
copy_file(
|
||||||
|
name = "generated_file",
|
||||||
|
src = "testdata/f1",
|
||||||
|
out = "dir/generated",
|
||||||
|
)
|
||||||
|
|
||||||
|
directory_test_suite(
|
||||||
|
name = "directory_tests",
|
||||||
|
)
|
||||||
|
|
||||||
|
subdirectory_test_suite(
|
||||||
|
name = "subdirectory_tests",
|
||||||
|
)
|
|
@ -0,0 +1,158 @@
|
||||||
|
# Copyright 2024 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 the directory rule."""
|
||||||
|
|
||||||
|
load("@bazel_skylib//rules/directory:directory.bzl", "directory")
|
||||||
|
load("@bazel_skylib//rules/directory:providers.bzl", "DirectoryInfo")
|
||||||
|
load("@rules_testing//lib:analysis_test.bzl", "analysis_test", "test_suite")
|
||||||
|
load("@rules_testing//lib:truth.bzl", "matching")
|
||||||
|
load(":utils.bzl", "directory_subject", "failure_matching", "failure_test")
|
||||||
|
|
||||||
|
def _source_root_test(name):
|
||||||
|
analysis_test(
|
||||||
|
name = name,
|
||||||
|
impl = _source_root_test_impl,
|
||||||
|
targets = {
|
||||||
|
"root": ":root",
|
||||||
|
"f1": ":f1_filegroup",
|
||||||
|
"f2": ":f2_filegroup",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def _source_root_test_impl(env, targets):
|
||||||
|
f1 = targets.f1.files.to_list()[0]
|
||||||
|
f2 = targets.f2.files.to_list()[0]
|
||||||
|
|
||||||
|
env.expect.that_collection(targets.root.files.to_list()).contains_exactly(
|
||||||
|
[f1, f2],
|
||||||
|
)
|
||||||
|
|
||||||
|
human_readable = str(targets.root.label)
|
||||||
|
|
||||||
|
root = directory_subject(env, targets.root[DirectoryInfo])
|
||||||
|
root.entries().keys().contains_exactly(["testdata"])
|
||||||
|
root.transitive_files().contains_exactly([f1, f2]).in_order()
|
||||||
|
root.human_readable().equals(human_readable)
|
||||||
|
env.expect.that_str(root.actual.path + "/testdata/f1").equals(f1.path)
|
||||||
|
|
||||||
|
testdata = directory_subject(env, root.actual.entries["testdata"])
|
||||||
|
testdata.entries().keys().contains_exactly(["f1", "subdir"])
|
||||||
|
testdata.human_readable().equals(human_readable + "/testdata")
|
||||||
|
|
||||||
|
subdir = directory_subject(env, testdata.actual.entries["subdir"])
|
||||||
|
subdir.entries().contains_exactly({"f2": f2})
|
||||||
|
subdir.transitive_files().contains_exactly([f2])
|
||||||
|
env.expect.that_str(subdir.actual.path + "/f2").equals(f2.path)
|
||||||
|
|
||||||
|
def _generated_root_test(name):
|
||||||
|
subject_name = "_%s_subject" % name
|
||||||
|
directory(
|
||||||
|
name = subject_name,
|
||||||
|
srcs = [":generated_file"],
|
||||||
|
)
|
||||||
|
|
||||||
|
analysis_test(
|
||||||
|
name = name,
|
||||||
|
impl = _generated_root_test_impl,
|
||||||
|
targets = {
|
||||||
|
"root": subject_name,
|
||||||
|
"generated": ":generated_file",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def _generated_root_test_impl(env, targets):
|
||||||
|
generated = targets.generated.files.to_list()[0]
|
||||||
|
|
||||||
|
env.expect.that_collection(targets.root.files.to_list()).contains_exactly(
|
||||||
|
[generated],
|
||||||
|
)
|
||||||
|
|
||||||
|
human_readable = str(targets.root.label)
|
||||||
|
|
||||||
|
root = directory_subject(env, targets.root[DirectoryInfo])
|
||||||
|
root.entries().keys().contains_exactly(["dir"])
|
||||||
|
root.transitive_files().contains_exactly([generated]).in_order()
|
||||||
|
root.human_readable().equals(human_readable)
|
||||||
|
env.expect.that_str(root.actual.path + "/dir/generated").equals(generated.path)
|
||||||
|
|
||||||
|
dir = directory_subject(env, root.actual.entries["dir"])
|
||||||
|
dir.human_readable().equals(human_readable + "/dir")
|
||||||
|
dir.entries().contains_exactly({"generated": generated})
|
||||||
|
dir.transitive_files().contains_exactly([generated])
|
||||||
|
env.expect.that_str(dir.actual.path + "/generated").equals(generated.path)
|
||||||
|
|
||||||
|
def _no_srcs_test(name):
|
||||||
|
subject_name = "_%s_subject" % name
|
||||||
|
directory(
|
||||||
|
name = subject_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
analysis_test(
|
||||||
|
name = name,
|
||||||
|
impl = _no_srcs_test_impl,
|
||||||
|
targets = {
|
||||||
|
"root": subject_name,
|
||||||
|
"f1": ":f1_filegroup",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def _no_srcs_test_impl(env, targets):
|
||||||
|
f1 = targets.f1.files.to_list()[0]
|
||||||
|
|
||||||
|
env.expect.that_collection(targets.root.files.to_list()).contains_exactly([])
|
||||||
|
|
||||||
|
d = directory_subject(env, targets.root[DirectoryInfo])
|
||||||
|
d.entries().contains_exactly({})
|
||||||
|
env.expect.that_str(d.actual.path + "/testdata/f1").equals(f1.path)
|
||||||
|
|
||||||
|
def _directory_with_self_srcs_test(name):
|
||||||
|
failure_test(
|
||||||
|
name = name,
|
||||||
|
impl = failure_matching(matching.contains("tests/directory to start with")),
|
||||||
|
rule = directory,
|
||||||
|
srcs = ["."],
|
||||||
|
)
|
||||||
|
|
||||||
|
def _outside_testdata_test(name):
|
||||||
|
failure_test(
|
||||||
|
name = name,
|
||||||
|
impl = failure_matching(matching.contains("lib/paths.bzl to start with")),
|
||||||
|
rule = directory,
|
||||||
|
srcs = ["@bazel_skylib//lib:paths"],
|
||||||
|
)
|
||||||
|
|
||||||
|
def _source_and_generated_root_test(name):
|
||||||
|
failure_test(
|
||||||
|
name = name,
|
||||||
|
impl = failure_matching(matching.contains(
|
||||||
|
"Having both source and generated files in a single directory is unsupported",
|
||||||
|
)),
|
||||||
|
rule = directory,
|
||||||
|
srcs = ["f1", ":generated_file"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# buildifier: disable=function-docstring
|
||||||
|
def directory_test_suite(name):
|
||||||
|
test_suite(
|
||||||
|
name = name,
|
||||||
|
tests = [
|
||||||
|
_source_root_test,
|
||||||
|
_generated_root_test,
|
||||||
|
_no_srcs_test,
|
||||||
|
_directory_with_self_srcs_test,
|
||||||
|
_outside_testdata_test,
|
||||||
|
_source_and_generated_root_test,
|
||||||
|
],
|
||||||
|
)
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Copyright 2024 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.
|
||||||
|
|
||||||
|
"""Generates tests for the directory rules from outside the repository."""
|
||||||
|
|
||||||
|
def _external_directory_tests_impl(repo_ctx):
|
||||||
|
for f in repo_ctx.attr.files:
|
||||||
|
repo_ctx.symlink(repo_ctx.path(f), f.package + "/" + f.name)
|
||||||
|
|
||||||
|
# Directory paths work differently while inside and outside the repository.
|
||||||
|
# To properly test this, we copy all our test code to an external
|
||||||
|
# repository.
|
||||||
|
external_directory_tests = repository_rule(
|
||||||
|
implementation = _external_directory_tests_impl,
|
||||||
|
attrs = {
|
||||||
|
"files": attr.label_list(default = [
|
||||||
|
"//tests/directory:BUILD",
|
||||||
|
"//tests/directory:directory_test.bzl",
|
||||||
|
"//tests/directory:subdirectory_test.bzl",
|
||||||
|
"//tests/directory:testdata/f1",
|
||||||
|
"//tests/directory:testdata/subdir/f2",
|
||||||
|
"//tests/directory:utils.bzl",
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def _external_directory_tests_ext_impl(_module_ctx):
|
||||||
|
external_directory_tests(name = "external_directory_tests")
|
||||||
|
|
||||||
|
# use_repo_rule would be preferred, but it isn't supported in bazel 6.
|
||||||
|
external_directory_tests_ext = module_extension(
|
||||||
|
implementation = _external_directory_tests_ext_impl,
|
||||||
|
)
|
|
@ -0,0 +1,113 @@
|
||||||
|
# Copyright 2024 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 subdirectory rules."""
|
||||||
|
|
||||||
|
load("@bazel_skylib//rules/directory:providers.bzl", "DirectoryInfo")
|
||||||
|
load("@bazel_skylib//rules/directory:subdirectory.bzl", "subdirectory")
|
||||||
|
load("@rules_testing//lib:analysis_test.bzl", "analysis_test", "test_suite")
|
||||||
|
load("@rules_testing//lib:truth.bzl", "matching")
|
||||||
|
load(":utils.bzl", "failure_matching", "failure_test")
|
||||||
|
|
||||||
|
_NONEXISTENT_SUBDIRECTORY_ERR = """directory:root/testdata does not contain an entry named "nonexistent".
|
||||||
|
Instead, it contains the following entries:
|
||||||
|
f1
|
||||||
|
subdir
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _subdirectory_test(name):
|
||||||
|
testdata_name = "_%s_dir" % name
|
||||||
|
subdir_name = "_%s_subdir" % name
|
||||||
|
|
||||||
|
subdirectory(
|
||||||
|
name = testdata_name,
|
||||||
|
parent = ":root",
|
||||||
|
path = "testdata",
|
||||||
|
)
|
||||||
|
|
||||||
|
subdirectory(
|
||||||
|
name = subdir_name,
|
||||||
|
parent = ":root",
|
||||||
|
path = "testdata/subdir",
|
||||||
|
)
|
||||||
|
|
||||||
|
analysis_test(
|
||||||
|
name = name,
|
||||||
|
impl = _subdirectory_test_impl,
|
||||||
|
targets = {
|
||||||
|
"root": ":root",
|
||||||
|
"testdata": testdata_name,
|
||||||
|
"subdir": subdir_name,
|
||||||
|
"f1": ":f1_filegroup",
|
||||||
|
"f2": ":f2_filegroup",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def _subdirectory_test_impl(env, targets):
|
||||||
|
f1 = targets.f1.files.to_list()[0]
|
||||||
|
f2 = targets.f2.files.to_list()[0]
|
||||||
|
|
||||||
|
root = targets.root[DirectoryInfo]
|
||||||
|
want_dir = root.entries["testdata"]
|
||||||
|
want_subdir = want_dir.entries["subdir"]
|
||||||
|
|
||||||
|
# Use that_str because it supports equality checks. They're not strings.
|
||||||
|
env.expect.that_str(targets.testdata[DirectoryInfo]).equals(want_dir)
|
||||||
|
env.expect.that_str(targets.subdir[DirectoryInfo]).equals(want_subdir)
|
||||||
|
|
||||||
|
env.expect.that_collection(
|
||||||
|
targets.testdata.files.to_list(),
|
||||||
|
).contains_exactly([f1, f2])
|
||||||
|
env.expect.that_collection(
|
||||||
|
targets.subdir.files.to_list(),
|
||||||
|
).contains_exactly([f2])
|
||||||
|
|
||||||
|
def _nonexistent_subdirectory_test(name):
|
||||||
|
failure_test(
|
||||||
|
name = name,
|
||||||
|
impl = failure_matching(matching.contains(_NONEXISTENT_SUBDIRECTORY_ERR)),
|
||||||
|
rule = subdirectory,
|
||||||
|
parent = ":root",
|
||||||
|
path = "testdata/nonexistent",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _subdirectory_of_file_test(name):
|
||||||
|
failure_test(
|
||||||
|
name = name,
|
||||||
|
impl = failure_matching(matching.contains("testdata/f1 to have type Directory, but got File")),
|
||||||
|
rule = subdirectory,
|
||||||
|
parent = ":root",
|
||||||
|
path = "testdata/f1/foo",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _subdirectory_as_file_test(name):
|
||||||
|
failure_test(
|
||||||
|
name = name,
|
||||||
|
impl = failure_matching(matching.contains("testdata/f1 to have type Directory, but got File")),
|
||||||
|
rule = subdirectory,
|
||||||
|
parent = ":root",
|
||||||
|
path = "testdata/f1",
|
||||||
|
)
|
||||||
|
|
||||||
|
# buildifier: disable=function-docstring
|
||||||
|
def subdirectory_test_suite(name):
|
||||||
|
test_suite(
|
||||||
|
name = name,
|
||||||
|
tests = [
|
||||||
|
_subdirectory_test,
|
||||||
|
_nonexistent_subdirectory_test,
|
||||||
|
_subdirectory_as_file_test,
|
||||||
|
_subdirectory_of_file_test,
|
||||||
|
],
|
||||||
|
)
|
|
@ -0,0 +1,65 @@
|
||||||
|
# Copyright 2024 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.
|
||||||
|
|
||||||
|
"""Helper functions for testing directory rules."""
|
||||||
|
|
||||||
|
load("@rules_testing//lib:analysis_test.bzl", "analysis_test")
|
||||||
|
load("@rules_testing//lib:truth.bzl", "subjects")
|
||||||
|
load("@rules_testing//lib:util.bzl", "util")
|
||||||
|
|
||||||
|
_depset_as_list_subject = lambda value, *, meta: subjects.collection(
|
||||||
|
value.to_list(),
|
||||||
|
meta = meta,
|
||||||
|
)
|
||||||
|
|
||||||
|
directory_info_subject = lambda value, *, meta: subjects.struct(
|
||||||
|
value,
|
||||||
|
meta = meta,
|
||||||
|
attrs = dict(
|
||||||
|
entries = subjects.dict,
|
||||||
|
transitive_files = _depset_as_list_subject,
|
||||||
|
path = subjects.str,
|
||||||
|
human_readable = subjects.str,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def failure_matching(matcher):
|
||||||
|
def test(env, target):
|
||||||
|
env.expect.that_target(target).failures().contains_exactly_predicates([
|
||||||
|
matcher,
|
||||||
|
])
|
||||||
|
|
||||||
|
return test
|
||||||
|
|
||||||
|
def directory_subject(env, directory_info):
|
||||||
|
return env.expect.that_value(
|
||||||
|
value = directory_info,
|
||||||
|
expr = "DirectoryInfo(%r)" % directory_info.path,
|
||||||
|
factory = directory_info_subject,
|
||||||
|
)
|
||||||
|
|
||||||
|
def failure_test(*, name, impl, rule, **kwargs):
|
||||||
|
subject_name = "_%s_subject" % name
|
||||||
|
util.helper_target(
|
||||||
|
rule,
|
||||||
|
name = subject_name,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
analysis_test(
|
||||||
|
name = name,
|
||||||
|
expect_failure = True,
|
||||||
|
impl = impl,
|
||||||
|
target = subject_name,
|
||||||
|
)
|
|
@ -26,6 +26,7 @@ def _all_test(env):
|
||||||
"copy_directory",
|
"copy_directory",
|
||||||
"copy_file",
|
"copy_file",
|
||||||
"diff_test",
|
"diff_test",
|
||||||
|
"directory",
|
||||||
"expand_template",
|
"expand_template",
|
||||||
"select_file",
|
"select_file",
|
||||||
"write_file",
|
"write_file",
|
||||||
|
@ -44,6 +45,7 @@ def _all_test(env):
|
||||||
"common_settings",
|
"common_settings",
|
||||||
"copy_directory",
|
"copy_directory",
|
||||||
"copy_file",
|
"copy_file",
|
||||||
|
"directory",
|
||||||
"expand_template",
|
"expand_template",
|
||||||
"select_file",
|
"select_file",
|
||||||
"write_file",
|
"write_file",
|
||||||
|
|
Loading…
Reference in New Issue