Skip to content
This repository was archived by the owner on Dec 29, 2025. It is now read-only.

Commit 76ee148

Browse files
author
Jake Gutierrez
authored
Merge pull request #2 from pauldraper/pauldraper/black
Convert black to worker
2 parents c99a754 + 822b851 commit 76ee148

26 files changed

+614
-97
lines changed

.bazelversion

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
5.0.0-pre.20211011.2
1+
5.3.0

README.md

Lines changed: 215 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,205 @@
11
# rules_file
22

3-
Bazel rules for basic file operations, like creating directories, and formatting.
3+
Bazel rules for basic file operations, such as creating directories and formatting.
44

5-
## Example
5+
## Directory
66

7-
To use buildifier as a formatter,
7+
Create a directory from files:
8+
9+
```bzl
10+
load("@rules_file//file:rules.bzl", "directory")
11+
12+
directory(
13+
name = "example",
14+
srcs = glob(["**/*.txt"]),
15+
)
16+
```
17+
18+
Create a directory from a tarball:
19+
20+
```bzl
21+
load("@rules_file//file:rules.bzl", "untar")
22+
23+
untar(
24+
name = "example",
25+
src = "example.tar",
26+
)
27+
```
28+
29+
## Package-less Files
30+
31+
Access files without regard to package structure. This can be helpful for formatting or Bazel integration tests.
32+
33+
### Workspace Example
34+
35+
Create a new repository containing all workspace files.
836

937
**WORKSPACE.bazel**
1038

1139
```bzl
12-
workspace(name = "example")
40+
files(
41+
name = "files"
42+
build = "BUILD.file.bazel",
43+
root_file = "//:WORKSPACE.bazel",
44+
)
45+
```
46+
47+
**BAZEL.bazel**
48+
49+
```
50+
load("@rules_file//file:rules.bzl", "bazelrc_deleted_packages")
51+
52+
bazelrc_deleted_packages(
53+
name = "gen_bazelrc",
54+
output = "deleted.bazelrc",
55+
packages = ["@files//:packages"],
56+
)
57+
```
58+
59+
**files.bazel**
60+
61+
```bzl
62+
# note: files is the symlink to the workspace
63+
filegroup(
64+
name = "example",
65+
srcs = glob(["files/**/*.txt"]),
66+
visibility = ["//visibility:public"],
67+
)
68+
```
69+
70+
Generate deleted.bazelrc:
71+
72+
```
73+
bazel run :gen_bazelrc
74+
```
75+
76+
**.bazelrc**
77+
78+
```
79+
import %workspace%/deleted.bazelrc
80+
```
81+
82+
Now `@files//:example` is all \*.txt files in the workspace.
83+
84+
### Bazel Integration Test Example
85+
86+
Use files in the test directory as data for a Bazel integration test.
87+
88+
**BAZEL.bazel**
89+
90+
```
91+
load("@rules_file//file:rules.bzl", "bazelrc_deleted_packages", "find_packages")
92+
93+
filegroup(
94+
name = "test",
95+
srcs = glob(["test/**/*.bazel" "test/**/*.txt"]),
96+
)
97+
98+
find_packages(
99+
name = "test_packages",
100+
roots = ["test"],
101+
)
102+
103+
bazelrc_deleted_packages(
104+
name = "gen_bazelrc",
105+
output = "deleted.bazelrc",
106+
packages = [":test_packages"],
107+
)
108+
```
109+
110+
Generate `deleted.bazelrc` by running:
111+
112+
```
113+
bazel run :gen_bazelrc
114+
```
13115

14-
load("@rules_file//file:workspace.bzl", "files")
116+
**.bazelrc**
117+
118+
```
119+
import %workspace%/deleted.bazelrc
120+
```
121+
122+
## Generate
123+
124+
In some cases, it is necessary to version control build products in the workspace (bootstrapping, working with other tools).
125+
126+
These rules build the outputs, and copy them to the workspace or check for differences.
127+
128+
**BUILD.bazel**
129+
130+
```bzl
131+
load("@rules_file//file:rules.bzl", "bazelrc_deleted_packages")
132+
load("@rules_file//file:rules.bzl", "generate", "generate_test")
133+
134+
genrule(
135+
name = "example",
136+
cmd = "echo GENERATED! > $@",
137+
outs = ["out/example.txt"],
138+
)
139+
140+
generate(
141+
name = "example_gen",
142+
srcs = "example.txt",
143+
data = ["out/example.txt"],
144+
data_strip_prefix = "out",
145+
)
146+
147+
generate_test(
148+
name = "example_diff",
149+
generate = ":example_gen",
150+
)
151+
152+
bazelrc_deleted_packages(
153+
name = "gen_bazelrc",
154+
output = "deleted.bazelrc",
155+
packages = ["@files//:packages"],
156+
)
157+
```
158+
159+
160+
161+
To overwrite the workspace file:
162+
163+
```bzl
164+
bazel run :example_gen
165+
```
166+
167+
To check for differences (e.g. in CI):
168+
169+
```bzl
170+
bazel test :example_diff
171+
```
172+
173+
## Format
174+
175+
Formatting is a particular case of the checked-in build products pattern.
176+
177+
The code formatting is a regular Bazel action. The formatted result can be using to overwrite workspace files, or to check for differences.
178+
179+
This repository has rules for buildifier, black, and gofmt. It is also used for [prettier](https://github.com/rivethealth/rules_javascript).
180+
181+
### Buildifier Example
182+
183+
**WORKSPACE.bazel**
184+
185+
```bzl
186+
BUILDTOOLS_VERSION = "3.5.0"
187+
188+
http_archive(
189+
name = "com_github_bazelbuild_buildtools",
190+
sha256 = "f5b666935a827bc2b6e2ca86ea56c796d47f2821c2ff30452d270e51c2a49708",
191+
strip_prefix = "buildtools-%s" % BUILDTOOLS_VERSION,
192+
url = "https://github.com/bazelbuild/buildtools/archive/%s.zip" % BUILDTOOLS_VERSION,
193+
)
194+
195+
load("@com_github_bazelbuild_buildtools//buildifier:deps.bzl", "buildifier_dependencies")
196+
197+
buildifier_dependencies()
15198

16199
files(
17-
name = "example_files"
200+
name = "files"
18201
build = "BUILD.file.bazel",
19-
root_file = "@example//WORKSPACE.bazel",
202+
root_file = "//:WORKSPACE.bazel",
20203
)
21204
```
22205

@@ -31,23 +214,21 @@ buildifier(
31214
)
32215

33216
format(
34-
name = "format",
35-
srcs = ["@example_files//:buildifier_files"],
217+
name = "buildifier_format",
218+
srcs = ["@files//:buildifier_files"],
36219
formatter = ":buildifier",
37220
strip_prefix = "files",
38221
)
39222

40223
generate_test(
41-
name = "test",
224+
name = "buildifier_diff",
42225
generate = ":format",
43226
)
44227
```
45228

46-
**BUILD.file.bazel**
229+
**files.bazel**
47230

48231
```bzl
49-
package(default_visibility = ["//visibility:public"])
50-
51232
filegroup(
52233
name = "buildifier_files",
53234
srcs = glob(
@@ -57,23 +238,31 @@ filegroup(
57238
"files/**/BUILD",
58239
"files/**/WORKSPACE",
59240
],
60-
["**/bazel-*/**"],
61241
),
242+
visibility = ["//visibility:public"],
62243
)
63244
```
64245

65-
Then run
246+
Generate deleted.bazelrc:
247+
248+
```
249+
bazel run :gen_bazelrc
250+
```
251+
252+
**.bazelrc**
253+
254+
```
255+
import %workspace%/deleted.bazelrc
256+
```
257+
258+
To format:
259+
260+
```sh
261+
bazel run :buildifier_format
262+
```
263+
264+
To check format:
66265

67266
```sh
68-
packages="$(\
69-
find .-name BUILD -o -name BUILD.bazel \
70-
| sed -e 's:/[^/]*$::' -e 's:^\./::' -e 's:^:@example_files//files/:' -e 's:/.$::' \
71-
| tr '\n' , \
72-
| sed 's/,$//' \
73-
)"
74-
75-
# format
76-
bazel run "--deleted_packages=$packages" //:format
77-
# test
78-
bazel run "--deleted_packages=$packages" //:test
267+
bazel run :buildifier_diff
79268
```

WORKSPACE.bazel

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,24 @@ load("@com_github_bazelbuild_buildtools//buildifier:deps.bzl", "buildifier_depen
7373

7474
buildifier_dependencies()
7575

76+
# Rivet Bazel Util
77+
RIVET_BAZEL_UTIL_VERSION = "f0c1bbe0935c3994e4c1ea4b3370a47d8df9819e"
78+
79+
http_archive(
80+
name = "rivet_bazel_util",
81+
sha256 = "3d89e5037d34e7fb5202f7ca7d6b26e008de1c27e97bd0c257fa362c3d61240e",
82+
strip_prefix = "rivet-bazel-util-%s" % RIVET_BAZEL_UTIL_VERSION,
83+
url = "https://github.com/rivethealth/rivet-bazel-util/archive/%s.zip" % RIVET_BAZEL_UTIL_VERSION,
84+
)
85+
7686
# Format
7787

7888
load("//file:workspace.bzl", "files")
7989

8090
files(
81-
name = "rules_file_files",
82-
build = "@rules_file//:BUILD.file.bazel",
83-
root_file = "@rules_file//:WORKSPACE.bazel",
91+
name = "files",
92+
build = "@rules_file//:files.bazel",
93+
root_file = "//:WORKSPACE.bazel",
8494
)
8595

8696
load("//rules:init.bzl", "file_init")

black/format/BUILD.bazel

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package(default_visibility = ["//visibility:public"])
2+
3+
filegroup(
4+
name = "src",
5+
srcs = glob(["src/**/*.py"]),
6+
)

black/format/src/__main__.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import argparse
2+
import black
3+
import contextlib
4+
import io
5+
import pathlib
6+
import setproctitle
7+
import sys
8+
from bazelwatcher2 import worker
9+
10+
setproctitle.setproctitle("black-worker")
11+
12+
parser = argparse.ArgumentParser(prog="black-worker")
13+
parser.add_argument("--black-option", action="append", default=[], dest="black_options")
14+
parser.add_argument("src", type=pathlib.Path)
15+
parser.add_argument("dest", type=pathlib.Path)
16+
17+
18+
@contextlib.contextmanager
19+
def _detach_wrapper(wrapper):
20+
try:
21+
yield wrapper
22+
finally:
23+
wrapper.detach()
24+
25+
26+
@contextlib.contextmanager
27+
def _modify_std():
28+
real_stdin = sys.stdin
29+
real_stderr = sys.stderr
30+
real_stdout = sys.stdout
31+
try:
32+
yield
33+
finally:
34+
sys.stdin = real_stdin
35+
sys.stdout = real_stdout
36+
sys.stderr = real_stderr
37+
38+
39+
def _run(args):
40+
args = parser.parse_args(args)
41+
42+
output = io.BytesIO()
43+
try:
44+
with _modify_std(), args.src.open() as stdin, args.dest.open(
45+
"w"
46+
) as stdout, _detach_wrapper(
47+
io.TextIOWrapper(output, write_through=True)
48+
) as stderr:
49+
sys.stdin = stdin
50+
sys.stdout = stdout
51+
sys.stderr = stderr
52+
black.main(
53+
args.black_options + ["-q", "--stdin-filename", str(args.src), "-"]
54+
)
55+
except SystemExit as e:
56+
exit_code = e.code
57+
else:
58+
exit_code = 0
59+
60+
return worker.WorkResult(exit_code=exit_code, output=output.getvalue())
61+
62+
63+
def _worker_factory(_):
64+
return lambda args, _: _run(args)
65+
66+
67+
worker.main(_worker_factory)

0 commit comments

Comments
 (0)