mirror of
https://github.com/bazel-contrib/bazel-lib
synced 2024-11-30 01:41:21 +00:00
132 lines
4.1 KiB
Python
132 lines
4.1 KiB
Python
|
"Public API for write_source_files"
|
||
|
|
||
|
load("//lib/private:write_source_files.bzl", _lib = "write_source_files_lib")
|
||
|
load("//lib:utils.bzl", _to_label = "to_label")
|
||
|
load("@bazel_skylib//rules:diff_test.bzl", _diff_test = "diff_test")
|
||
|
load("//lib/private:fail_with_message_test.bzl", "fail_with_message_test")
|
||
|
|
||
|
_write_source_files = rule(
|
||
|
attrs = _lib.attrs,
|
||
|
implementation = _lib.implementation,
|
||
|
executable = True,
|
||
|
)
|
||
|
|
||
|
def write_source_files(name, files, **kwargs):
|
||
|
"""Write to one or more files in the source tree. Stamp out tests that ensure the files exists and are up to date.
|
||
|
|
||
|
Usage:
|
||
|
|
||
|
```starlark
|
||
|
load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files")
|
||
|
|
||
|
write_source_files(
|
||
|
name = "write_foobar",
|
||
|
files = {
|
||
|
"foobar.json": "//some/generated:file",
|
||
|
},
|
||
|
)
|
||
|
```
|
||
|
|
||
|
To update the source file, run:
|
||
|
```bash
|
||
|
bazel run //:write_foobar
|
||
|
```
|
||
|
|
||
|
A test will fail if the source file doesn't exist
|
||
|
```bash
|
||
|
bazel test //...
|
||
|
|
||
|
//:foobar.json does not exist. To create & update this file, run:
|
||
|
|
||
|
bazel run //:write_foobar
|
||
|
```
|
||
|
|
||
|
...or if it's out of date.
|
||
|
```bash
|
||
|
bazel test //...
|
||
|
|
||
|
//:foobar.json is out-of-date. To update this file, run:
|
||
|
|
||
|
bazel run //:write_foobar
|
||
|
```
|
||
|
|
||
|
Args:
|
||
|
name: Name of the executable target that creates or updates the source file
|
||
|
files: A dict where the keys are source files to write to and the values are labels pointing to the desired content.
|
||
|
Source files must be within the same bazel package as the target.
|
||
|
**kwargs: Other common named parameters such as `tags` or `visibility`
|
||
|
"""
|
||
|
|
||
|
out_files = files.keys()
|
||
|
in_files = [files[f] for f in out_files]
|
||
|
|
||
|
# Stamp an executable rule that writes to the out file
|
||
|
_write_source_files(
|
||
|
name = name,
|
||
|
in_files = in_files,
|
||
|
out_files = out_files,
|
||
|
is_windows = select({
|
||
|
"@bazel_tools//src/conditions:host_windows": True,
|
||
|
"//conditions:default": False,
|
||
|
}),
|
||
|
visibility = kwargs.get("visibility"),
|
||
|
tags = kwargs.get("tags"),
|
||
|
)
|
||
|
|
||
|
# Fail if user passes args that would conflict with stamped out targets below
|
||
|
if kwargs.pop("file1", None) != None:
|
||
|
fail("file1 not a valid parameter in write_source_file")
|
||
|
if kwargs.pop("file2", None) != None:
|
||
|
fail("file2 not a valid parameter in write_source_file")
|
||
|
if kwargs.pop("failure_message", None) != None:
|
||
|
fail("failure_message not a valid parameter in write_source_file")
|
||
|
|
||
|
for i in range(len(out_files)):
|
||
|
out_file = _to_label(out_files[i])
|
||
|
out_file_missing = _is_file_missing(out_file)
|
||
|
|
||
|
name_test = "%s_%d_test" % (name, i)
|
||
|
|
||
|
if out_file_missing:
|
||
|
# Stamp out a test that fails with a helpful message when the source file doesn't exist.
|
||
|
# Note that we cannot simply call fail() here since it will fail during the analysis
|
||
|
# phase and prevent the user from calling bazel run //update/the:file.
|
||
|
fail_with_message_test(
|
||
|
name = name_test,
|
||
|
message = """
|
||
|
|
||
|
%s does not exist. To create & update this file, run:
|
||
|
|
||
|
bazel run //%s:%s
|
||
|
|
||
|
""" % (out_file, native.package_name(), name),
|
||
|
visibility = kwargs.get("visibility"),
|
||
|
tags = kwargs.get("tags"),
|
||
|
)
|
||
|
else:
|
||
|
# Stamp out a diff test the check that the source file is up to date
|
||
|
_diff_test(
|
||
|
name = name_test,
|
||
|
file1 = in_files[i],
|
||
|
file2 = out_file,
|
||
|
failure_message = """
|
||
|
|
||
|
%s is out-of-date. To update this file, run:
|
||
|
|
||
|
bazel run //%s:%s
|
||
|
|
||
|
""" % (out_file, native.package_name(), name),
|
||
|
**kwargs
|
||
|
)
|
||
|
|
||
|
def _is_file_missing(label):
|
||
|
"""Check if a file is missing by passing its relative path through a glob()
|
||
|
|
||
|
Args
|
||
|
label: the file's label
|
||
|
"""
|
||
|
file_abs = "%s/%s" % (label.package, label.name)
|
||
|
file_rel = file_abs[len(native.package_name()) + 1:]
|
||
|
file_glob = native.glob([file_rel])
|
||
|
return len(file_glob) == 0
|