-
Notifications
You must be signed in to change notification settings - Fork 11k
Expand file tree
/
Copy pathdefs.bzl
More file actions
375 lines (340 loc) · 14.6 KB
/
defs.bzl
File metadata and controls
375 lines (340 loc) · 14.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
load("@crates//:data.bzl", "DEP_DATA")
load("@crates//:defs.bzl", "all_crate_deps")
load("@rules_platform//platform_data:defs.bzl", "platform_data")
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_proc_macro", "rust_test")
load("@rules_rust//cargo/private:cargo_build_script_wrapper.bzl", "cargo_build_script")
PLATFORMS = [
"linux_arm64_musl",
"linux_amd64_musl",
"macos_amd64",
"macos_arm64",
"windows_amd64",
"windows_arm64",
]
# Match Cargo's Windows linker behavior so Bazel-built binaries and tests use
# the same stack reserve on both Windows ABIs and resolve UCRT imports on MSVC.
WINDOWS_RUSTC_LINK_FLAGS = select({
"@rules_rs//rs/experimental/platforms/constraints:windows_gnullvm": [
"-C",
"link-arg=-Wl,--stack,8388608", # 8 MiB
],
"@rules_rs//rs/experimental/platforms/constraints:windows_msvc": [
"-C",
"link-arg=/STACK:8388608", # 8 MiB
"-C",
"link-arg=/NODEFAULTLIB:libucrt.lib",
"-C",
"link-arg=ucrt.lib",
],
"//conditions:default": [],
})
# libwebrtc uses Objective-C categories from native archives. Any Bazel-linked
# macOS binary/test that can pull it in must keep category symbols alive.
MACOS_WEBRTC_RUSTC_LINK_FLAGS = select({
"@platforms//os:macos": [
"-C",
"link-arg=-ObjC",
"-C",
"link-arg=-lc++",
],
"//conditions:default": [],
})
def multiplatform_binaries(name, platforms = PLATFORMS):
for platform in platforms:
platform_data(
name = name + "_" + platform,
platform = "@llvm//platforms:" + platform,
target = name,
tags = ["manual"],
)
native.filegroup(
name = "release_binaries",
srcs = [name + "_" + platform for platform in platforms],
tags = ["manual"],
)
def _workspace_root_test_impl(ctx):
is_windows = ctx.target_platform_has_constraint(ctx.attr._windows_constraint[platform_common.ConstraintValueInfo])
launcher = ctx.actions.declare_file(ctx.label.name + ".bat" if is_windows else ctx.label.name)
test_bin = ctx.executable.test_bin
workspace_root_marker = ctx.file.workspace_root_marker
launcher_template = ctx.file._windows_launcher_template if is_windows else ctx.file._bash_launcher_template
ctx.actions.expand_template(
template = launcher_template,
output = launcher,
is_executable = True,
substitutions = {
"__TEST_BIN__": test_bin.short_path,
"__WORKSPACE_ROOT_MARKER__": workspace_root_marker.short_path,
},
)
runfiles = ctx.runfiles(files = [test_bin, workspace_root_marker]).merge(ctx.attr.test_bin[DefaultInfo].default_runfiles)
for data_dep in ctx.attr.data:
runfiles = runfiles.merge(ctx.runfiles(files = data_dep[DefaultInfo].files.to_list()))
runfiles = runfiles.merge(data_dep[DefaultInfo].default_runfiles)
return [
DefaultInfo(
executable = launcher,
files = depset([launcher]),
runfiles = runfiles,
),
RunEnvironmentInfo(
environment = ctx.attr.env,
),
]
workspace_root_test = rule(
implementation = _workspace_root_test_impl,
test = True,
attrs = {
"data": attr.label_list(
allow_files = True,
),
"env": attr.string_dict(),
"test_bin": attr.label(
cfg = "target",
executable = True,
mandatory = True,
),
"workspace_root_marker": attr.label(
allow_single_file = True,
mandatory = True,
),
"_windows_constraint": attr.label(
default = "@platforms//os:windows",
providers = [platform_common.ConstraintValueInfo],
),
"_bash_launcher_template": attr.label(
allow_single_file = True,
default = "//:workspace_root_test_launcher.sh.tpl",
),
"_windows_launcher_template": attr.label(
allow_single_file = True,
default = "//:workspace_root_test_launcher.bat.tpl",
),
},
)
def codex_rust_crate(
name,
crate_name,
crate_features = [],
crate_srcs = None,
crate_edition = None,
proc_macro = False,
build_script_enabled = True,
build_script_data = [],
compile_data = [],
lib_data_extra = [],
rustc_flags_extra = [],
rustc_env = {},
deps_extra = [],
integration_compile_data_extra = [],
integration_test_args = [],
integration_test_timeout = None,
test_data_extra = [],
test_shard_counts = {},
test_tags = [],
unit_test_timeout = None,
extra_binaries = []):
"""Defines a Rust crate with library, binaries, and tests wired for Bazel + Cargo parity.
The macro mirrors Cargo conventions: it builds a library when `src/` exists,
wires build scripts, exports `CARGO_BIN_EXE_*` for integration tests, and
creates unit + integration test targets. Dependency buckets map to the
Cargo.lock resolution in `@crates`.
Args:
name: Bazel target name for the library, should be the directory name.
Example: `app-server`.
crate_name: Cargo crate name from Cargo.toml
Example: `codex_app_server`.
crate_features: Cargo features to enable for this crate.
Crates are only compiled in a single configuration across the workspace, i.e.
with all features in this list enabled. So use sparingly, and prefer to refactor
optional functionality to a separate crate.
crate_srcs: Optional explicit srcs; defaults to `src/**/*.rs`.
crate_edition: Rust edition override, if not default.
You probably don't want this, it's only here for a single caller.
proc_macro: Whether this crate builds a proc-macro library.
build_script_data: Data files exposed to the build script at runtime.
compile_data: Non-Rust compile-time data for the library target.
lib_data_extra: Extra runtime data for the library target.
rustc_env: Extra rustc_env entries to merge with defaults.
deps_extra: Extra normal deps beyond @crates resolution.
Typically only needed when features add additional deps.
integration_compile_data_extra: Extra compile_data for integration tests.
integration_test_args: Optional args for integration test binaries.
integration_test_timeout: Optional Bazel timeout for integration test
targets generated from `tests/*.rs`.
test_data_extra: Extra runtime data for tests.
test_shard_counts: Mapping from generated test target name to Bazel
shard count. Matching tests use native Bazel sharding on the
original test label, while rules_rust assigns each Rust test case
to a stable bucket by hashing the test name. Matching tests are
also marked flaky, which gives them Bazel's default three attempts.
test_tags: Tags applied to unit + integration test targets.
Typically used to disable the sandbox, but see https://bazel.build/reference/be/common-definitions#common.tags
unit_test_timeout: Optional Bazel timeout for the unit-test target
generated from `src/**/*.rs`.
extra_binaries: Additional binary labels to surface as test data and
`CARGO_BIN_EXE_*` environment variables. These are only needed for binaries from a different crate.
"""
test_env = {
# The launcher resolves an absolute workspace root at runtime so
# manifest-only platforms like macOS still point Insta at the real
# `codex-rs` checkout.
"INSTA_WORKSPACE_ROOT": ".",
"INSTA_SNAPSHOT_PATH": "src",
}
native.filegroup(
name = "package-files",
srcs = native.glob(
["**"],
exclude = [
"**/BUILD.bazel",
"BUILD.bazel",
"target/**",
],
allow_empty = True,
),
visibility = ["//visibility:public"],
)
rustc_env = {
"BAZEL_PACKAGE": native.package_name(),
} | rustc_env
manifest_relpath = native.package_name()
if manifest_relpath.startswith("codex-rs/"):
manifest_relpath = manifest_relpath[len("codex-rs/"):]
manifest_path = manifest_relpath + "/Cargo.toml"
binaries = DEP_DATA.get(native.package_name())["binaries"]
lib_srcs = crate_srcs or native.glob(["src/**/*.rs"], exclude = binaries.values(), allow_empty = True)
maybe_deps = []
if build_script_enabled and native.glob(["build.rs"], allow_empty = True):
cargo_build_script(
name = name + "-build-script",
srcs = ["build.rs"],
deps = all_crate_deps(build = True),
data = build_script_data,
# Some build script deps sniff version-related env vars...
version = "0.0.0",
)
maybe_deps += [name + "-build-script"]
if lib_srcs:
lib_rule = rust_proc_macro if proc_macro else rust_library
lib_rule(
name = name,
crate_name = crate_name,
crate_features = crate_features,
deps = all_crate_deps() + maybe_deps + deps_extra,
compile_data = compile_data,
data = lib_data_extra,
srcs = lib_srcs,
edition = crate_edition,
rustc_flags = rustc_flags_extra,
rustc_env = rustc_env,
visibility = ["//visibility:public"],
)
unit_test_name = name + "-unit-tests"
unit_test_binary = name + "-unit-tests-bin"
unit_test_shard_count = _test_shard_count(test_shard_counts, unit_test_name)
# Shard at the workspace_root_test layer. rules_rust's sharding wrapper
# expects to run from its own runfiles cwd, while workspace_root_test
# deliberately changes cwd so Insta sees Cargo-like snapshot paths.
rust_test(
name = unit_test_binary,
crate = name,
deps = all_crate_deps(normal = True, normal_dev = True) + maybe_deps + deps_extra,
# Unit tests also compile to standalone Windows executables, so
# keep their stack reserve aligned with binaries and integration
# tests under gnullvm.
# Bazel has emitted both `codex-rs/<crate>/...` and
# `../codex-rs/<crate>/...` paths for `file!()`. Strip either
# prefix so the workspace-root launcher sees Cargo-like metadata
# such as `tui/src/...`.
rustc_flags = rustc_flags_extra + WINDOWS_RUSTC_LINK_FLAGS + [
"--remap-path-prefix=../codex-rs=",
"--remap-path-prefix=codex-rs=",
],
rustc_env = rustc_env,
data = test_data_extra,
tags = test_tags + ["manual"],
)
unit_test_kwargs = {}
if unit_test_timeout:
unit_test_kwargs["timeout"] = unit_test_timeout
if unit_test_shard_count:
unit_test_kwargs["shard_count"] = unit_test_shard_count
unit_test_kwargs["flaky"] = True
workspace_root_test(
name = unit_test_name,
env = test_env,
test_bin = ":" + unit_test_binary,
workspace_root_marker = "//codex-rs/utils/cargo-bin:repo_root.marker",
tags = test_tags,
**unit_test_kwargs
)
maybe_deps += [name]
sanitized_binaries = []
cargo_env = {}
for binary, main in binaries.items():
#binary = binary.replace("-", "_")
sanitized_binaries.append(binary)
cargo_env["CARGO_BIN_EXE_" + binary] = "$(rlocationpath :%s)" % binary
rust_binary(
name = binary,
crate_name = binary.replace("-", "_"),
crate_root = main,
deps = all_crate_deps() + maybe_deps + deps_extra,
edition = crate_edition,
rustc_flags = rustc_flags_extra + WINDOWS_RUSTC_LINK_FLAGS,
srcs = native.glob(["src/**/*.rs"]),
visibility = ["//visibility:public"],
)
for binary_label in extra_binaries:
sanitized_binaries.append(binary_label)
binary = Label(binary_label).name
cargo_env["CARGO_BIN_EXE_" + binary] = "$(rlocationpath %s)" % binary_label
integration_test_kwargs = {}
if integration_test_args:
integration_test_kwargs["args"] = integration_test_args
if integration_test_timeout:
integration_test_kwargs["timeout"] = integration_test_timeout
for test in native.glob(["tests/*.rs"], allow_empty = True):
test_file_stem = test.removeprefix("tests/").removesuffix(".rs")
test_crate_name = test_file_stem.replace("-", "_")
test_name = name + "-" + test_file_stem.replace("/", "-")
if not test_name.endswith("-test"):
test_name += "-test"
test_kwargs = {}
test_kwargs.update(integration_test_kwargs)
test_shard_count = _test_shard_count(test_shard_counts, test_name)
if test_shard_count:
test_kwargs["experimental_enable_sharding"] = True
test_kwargs["shard_count"] = test_shard_count
test_kwargs["flaky"] = True
rust_test(
name = test_name,
crate_name = test_crate_name,
crate_root = test,
srcs = [test],
data = native.glob(["tests/**"], allow_empty = True) + sanitized_binaries + test_data_extra,
compile_data = native.glob(["tests/**"], allow_empty = True) + integration_compile_data_extra,
deps = all_crate_deps(normal = True, normal_dev = True) + maybe_deps + deps_extra,
# Bazel has emitted both `codex-rs/<crate>/...` and
# `../codex-rs/<crate>/...` paths for `file!()`. Strip either
# prefix so Insta records Cargo-like metadata such as `core/tests/...`.
rustc_flags = rustc_flags_extra + WINDOWS_RUSTC_LINK_FLAGS + [
"--remap-path-prefix=../codex-rs=",
"--remap-path-prefix=codex-rs=",
],
rustc_env = rustc_env,
# Important: do not merge `test_env` here. Its unit-test-only
# `INSTA_WORKSPACE_ROOT="codex-rs"` is tuned for unit tests that
# execute from the repo root and can misplace integration snapshots.
env = cargo_env,
tags = test_tags,
**test_kwargs
)
def _test_shard_count(test_shard_counts, test_name):
shard_count = test_shard_counts.get(test_name)
if shard_count == None:
return None
if shard_count < 1:
fail("test_shard_counts[{}] must be a positive integer".format(test_name))
return shard_count