184 lines
6.3 KiB
Python
184 lines
6.3 KiB
Python
"""write_source_files provides a workaround for the restriction that `bazel build` cannot write to the source tree.
|
|
|
|
Read more about the philosophy of writing to the source tree: <https://blog.aspect.build/bazel-can-write-to-the-source-folder>
|
|
|
|
## 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
|
|
```
|
|
|
|
The generated `diff_test` will fail if the file is out of date and print out instructions on
|
|
how to update it.
|
|
|
|
If the file does not exist, Bazel will fail at analysis time and print out instructions on
|
|
how to create it.
|
|
|
|
You can declare a tree of generated source file targets:
|
|
|
|
```starlark
|
|
load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files")
|
|
|
|
write_source_files(
|
|
name = "write_all",
|
|
additional_update_targets = [
|
|
# Other write_source_files targets to run when this target is run
|
|
"//a/b/c:write_foo",
|
|
"//a/b:write_bar",
|
|
]
|
|
)
|
|
```
|
|
|
|
And update them with a single run:
|
|
|
|
```bash
|
|
bazel run //:write_all
|
|
```
|
|
|
|
When a file is out of date, you can leave a suggestion to run a target further up in the tree by specifying `suggested_update_target`.
|
|
For example,
|
|
|
|
```starlark
|
|
write_source_files(
|
|
name = "write_foo",
|
|
files = {
|
|
"foo.json": ":generated-foo",
|
|
},
|
|
suggested_update_target = "//:write_all"
|
|
)
|
|
```
|
|
|
|
A test failure from `foo.json` being out of date will yield the following message:
|
|
|
|
```
|
|
//a/b:c:foo.json is out of date. To update this and other generated files, run:
|
|
|
|
bazel run //:write_all
|
|
|
|
To update *only* this file, run:
|
|
|
|
bazel run //a/b/c:write_foo
|
|
```
|
|
|
|
If you have many `write_source_files` targets that you want to update as a group, we recommend wrapping
|
|
`write_source_files` in a macro that defaults `suggested_update_target` to the umbrella update target.
|
|
|
|
NOTE: If you run formatters or linters on your codebase, it is advised that you exclude/ignore the outputs of this
|
|
rule from those formatters/linters so as to avoid causing collisions and failing tests.
|
|
|
|
"""
|
|
|
|
load(
|
|
"//lib/private:write_source_file.bzl",
|
|
_WriteSourceFileInfo = "WriteSourceFileInfo",
|
|
_write_source_file = "write_source_file",
|
|
)
|
|
|
|
WriteSourceFileInfo = _WriteSourceFileInfo
|
|
|
|
def write_source_files(
|
|
name,
|
|
files = {},
|
|
executable = False,
|
|
additional_update_targets = [],
|
|
suggested_update_target = None,
|
|
diff_test = True,
|
|
check_that_out_file_exists = True,
|
|
**kwargs):
|
|
"""Write one or more files and/or directories to the source tree.
|
|
|
|
By default, `diff_test` targets are generated that ensure the source tree files and/or directories to be written to
|
|
are up to date and the rule also checks that all source tree files and/or directories to be written to exist.
|
|
To disable the exists check and up-to-date tests set `diff_test` to `False`.
|
|
|
|
Args:
|
|
name: Name of the runnable target that creates or updates the source tree files and/or directories.
|
|
|
|
files: A dict where the keys are files or directories in the source tree to write to and the values are labels
|
|
pointing to the desired content, typically file or directory outputs of other targets.
|
|
|
|
Destination files and directories must be within the same containing Bazel package as this target if
|
|
`check_that_out_file_exists` is True. See `check_that_out_file_exists` docstring for more info.
|
|
|
|
executable: Whether source tree files written should be made executable.
|
|
|
|
This applies to all source tree files written by this target. This attribute is not propagated to `additional_update_targets`.
|
|
|
|
To set different executable permissions on different source tree files use multiple `write_source_files` targets.
|
|
|
|
additional_update_targets: List of other `write_source_files` or `write_source_file` targets to call in the same run.
|
|
|
|
suggested_update_target: Label of the `write_source_files` or `write_source_file` target to suggest running when files are out of date.
|
|
|
|
diff_test: Test that the source tree files and/or directories exist and are up to date.
|
|
|
|
check_that_out_file_exists: Test that each output file exists and print a helpful error message if it doesn't.
|
|
|
|
If `True`, destination files and directories must be in the same containing Bazel package as the target since the underlying
|
|
mechanism for this check is limited to files in the same Bazel package.
|
|
|
|
**kwargs: Other common named parameters such as `tags` or `visibility`
|
|
"""
|
|
|
|
single_update_target = len(files.keys()) == 1
|
|
update_targets = []
|
|
test_targets = []
|
|
for i, pair in enumerate(files.items()):
|
|
out_file, in_file = pair
|
|
|
|
this_suggested_update_target = suggested_update_target
|
|
if single_update_target:
|
|
update_target_name = name
|
|
else:
|
|
update_target_name = "%s_%d" % (name, i)
|
|
update_targets.append(update_target_name)
|
|
if not this_suggested_update_target:
|
|
this_suggested_update_target = name
|
|
|
|
# Runnable target that writes to the out file to the source tree
|
|
test_target = _write_source_file(
|
|
name = update_target_name,
|
|
in_file = in_file,
|
|
out_file = out_file,
|
|
executable = executable,
|
|
additional_update_targets = additional_update_targets if single_update_target else [],
|
|
suggested_update_target = this_suggested_update_target,
|
|
diff_test = diff_test,
|
|
check_that_out_file_exists = check_that_out_file_exists,
|
|
**kwargs
|
|
)
|
|
|
|
if test_target:
|
|
test_targets.append(test_target)
|
|
|
|
if len(test_targets) > 0:
|
|
native.test_suite(
|
|
name = "%s_tests" % name,
|
|
tests = test_targets,
|
|
visibility = kwargs.get("visibility"),
|
|
tags = kwargs.get("tags"),
|
|
)
|
|
|
|
if not single_update_target:
|
|
_write_source_file(
|
|
name = name,
|
|
additional_update_targets = update_targets + additional_update_targets,
|
|
suggested_update_target = suggested_update_target,
|
|
**kwargs
|
|
)
|
|
|
|
write_source_file = _write_source_file
|