Skip to content

Commit d85c4b6

Browse files
authored
Fix binary-only crates (#132)
1 parent a8d7c3f commit d85c4b6

10 files changed

Lines changed: 491 additions & 31 deletions

rs/private/git_cargo_workspace_repository.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ crate(
3939
links = {links},
4040
build_script = {build_script},
4141
is_proc_macro = {is_proc_macro},
42+
has_lib = {has_lib},
4243
binaries = {binaries},
4344
package_metadata_bazel_deps = [
4445
{package_metadata_bazel_deps}
@@ -52,6 +53,7 @@ crate(
5253
links = cargo.values["links"],
5354
build_script = cargo.values["build_script"],
5455
is_proc_macro = cargo.values["is_proc_macro"],
56+
has_lib = cargo.values["has_lib"],
5557
binaries = cargo.values["binaries"],
5658
package_metadata_bazel_deps = _render_label_list(cargo.bazel_metadata.get("deps", [])),
5759
additive_build_file_content = additive_build_file_content,

rs/private/git_crate_metadata_repository.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ def crate(
1212
links,
1313
build_script,
1414
is_proc_macro,
15+
has_lib,
1516
binaries,
1617
package_metadata_bazel_deps):
1718
{rust_crate_call}""".format(
@@ -27,6 +28,7 @@ def crate(
2728
crate_name = "crate_name",
2829
crate_root = "crate_root",
2930
edition = "edition",
31+
has_lib = "has_lib",
3032
is_proc_macro = "is_proc_macro",
3133
links = "links",
3234
name = repr(rctx.attr.package_name),

rs/private/repository_utils.bzl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ def cargo_build_file_values(rctx, cargo_toml, gen_binaries, package_path = "", g
117117
lib = cargo_toml.get("lib", {})
118118
is_proc_macro = lib.get("proc-macro") or lib.get("proc_macro") or False
119119
crate_root = (lib.get("path") or "src/lib.rs").removeprefix("./")
120+
has_lib = "lib" in cargo_toml or package_dir.get_child(crate_root).exists
120121

121122
edition = package.get("edition", "2015")
122123
crate_name = lib.get("name")
@@ -148,6 +149,7 @@ def cargo_build_file_values(rctx, cargo_toml, gen_binaries, package_path = "", g
148149
"crate_name": repr(crate_name),
149150
"crate_root": repr(crate_root),
150151
"edition": repr(edition),
152+
"has_lib": repr(has_lib),
151153
"is_proc_macro": repr(is_proc_macro),
152154
"links": repr(links),
153155
},
@@ -186,6 +188,7 @@ _RUST_CRATE_MACRO_CALL = """{indent}rust_crate(
186188
{indent} build_script_tools = {build_script_tools}{conditional_build_script_tools},
187189
{indent} build_script_tags = {build_script_tags},
188190
{indent} is_proc_macro = {is_proc_macro},
191+
{indent} has_lib = {has_lib},
189192
{indent} binaries = {binaries},
190193
{indent} use_legacy_rules_rust_platforms = {use_legacy_rules_rust_platforms},
191194
{skip_deps_verification_attr}{indent})
@@ -257,6 +260,7 @@ def render_rust_crate_call(attr, values, bazel_metadata = {}, extra_deps = "", i
257260
conditional_build_script_tools = " + " + conditional_build_script_tools if conditional_build_script_tools else "",
258261
build_script_tags = repr(attr.build_script_tags),
259262
is_proc_macro = values["is_proc_macro"],
263+
has_lib = values["has_lib"],
260264
binaries = values["binaries"],
261265
use_legacy_rules_rust_platforms = use_legacy_rules_rust_platforms,
262266
skip_deps_verification_attr = skip_deps_verification_attr,

rs/rust_crate.bzl

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def rust_crate(
3939
build_script_tools,
4040
build_script_tags,
4141
is_proc_macro,
42+
has_lib,
4243
binaries,
4344
use_legacy_rules_rust_platforms,
4445
extra_compile_data = [],
@@ -139,43 +140,68 @@ def rust_crate(
139140

140141
deps = deps + maybe_build_script
141142

142-
kwargs = dict(
143-
name = name,
144-
crate_name = crate_name,
145-
version = version,
146-
srcs = srcs,
147-
compile_data = compile_data,
148-
aliases = aliases,
149-
deps = deps,
150-
data = data,
151-
crate_features = crate_features + select(
152-
{_platform(k, use_legacy_rules_rust_platforms): v for k, v in conditional_crate_features.items()} |
153-
{"//conditions:default": []},
154-
),
155-
crate_root = crate_root,
156-
edition = edition,
157-
rustc_env = rustc_env,
158-
rustc_env_files = ["cargo_toml_env_vars.env"],
159-
rustc_flags = rustc_flags + ["--cap-lints=allow"],
160-
tags = crate_tags,
161-
target_compatible_with = target_compatible_with,
162-
package_metadata = [name + "_package_metadata"],
163-
skip_deps_verification = skip_deps_verification,
164-
visibility = ["//visibility:public"],
165-
skip_per_crate_rustc_flags = True,
166-
)
143+
if not has_lib:
144+
# HACK: create a stub target so the hub's `<crate>-<version>` alias
145+
# (emitted unconditionally in rs/extensions.bzl) still resolves for
146+
# binary-only crates. Marked as incompatible so that library use
147+
# fails at analysis time. The descriptive stub name & alias make the
148+
# error self-explanatory.
149+
#
150+
# A cleaner fix would be to make the hub skip the library alias when
151+
# the crate has no library, but that is non-trivial.
152+
stub_name = name + "_no_library_only_binary"
153+
native.filegroup(
154+
name = stub_name,
155+
tags = crate_tags,
156+
target_compatible_with = ["@platforms//:incompatible"],
157+
visibility = ["//visibility:public"],
158+
)
159+
native.alias(
160+
name = name,
161+
actual = ":" + stub_name,
162+
tags = crate_tags,
163+
visibility = ["//visibility:public"],
164+
)
167165

168-
if is_proc_macro:
169-
(_rust_proc_macro if skip_deps_verification else rust_proc_macro)(**kwargs)
170-
else:
171-
(_rust_library if skip_deps_verification else rust_library)(**kwargs)
166+
if has_lib:
167+
kwargs = dict(
168+
name = name,
169+
crate_name = crate_name,
170+
version = version,
171+
srcs = srcs,
172+
compile_data = compile_data,
173+
aliases = aliases,
174+
deps = deps,
175+
data = data,
176+
crate_features = crate_features + select(
177+
{_platform(k, use_legacy_rules_rust_platforms): v for k, v in conditional_crate_features.items()} |
178+
{"//conditions:default": []},
179+
),
180+
crate_root = crate_root,
181+
edition = edition,
182+
rustc_env = rustc_env,
183+
rustc_env_files = ["cargo_toml_env_vars.env"],
184+
rustc_flags = rustc_flags + ["--cap-lints=allow"],
185+
tags = crate_tags,
186+
target_compatible_with = target_compatible_with,
187+
package_metadata = [name + "_package_metadata"],
188+
skip_deps_verification = skip_deps_verification,
189+
visibility = ["//visibility:public"],
190+
skip_per_crate_rustc_flags = True,
191+
)
192+
193+
if is_proc_macro:
194+
(_rust_proc_macro if skip_deps_verification else rust_proc_macro)(**kwargs)
195+
else:
196+
(_rust_library if skip_deps_verification else rust_library)(**kwargs)
172197

198+
binary_lib_dep = [name] if has_lib else []
173199
for binary, crate_root in binaries.items():
174200
rust_binary(
175201
name = binary + "__bin",
176202
compile_data = compile_data,
177203
aliases = aliases,
178-
deps = [name] + deps,
204+
deps = binary_lib_dep + deps,
179205
data = data,
180206
crate_features = crate_features,
181207
crate_root = crate_root,

test/BUILD.bazel

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,14 @@ DISABLED = [
6868
#"uv",
6969
]
7070

71+
# Tests whose `_workspace_deps` aggregation intentionally fails analysis.
72+
# Their binaries are referenced explicitly in :all_builds below.
73+
BINARY_ONLY_TESTS = ["binary_only"]
74+
7175
TESTS = [f for f in glob(
7276
["*"],
7377
exclude_directories = 0,
74-
) if "." not in f and "bazel-" not in f and f not in DISABLED]
78+
) if "." not in f and "bazel-" not in f and f not in DISABLED and f not in BINARY_ONLY_TESTS]
7579

7680
rust_binary(
7781
name = "rust_binary",
@@ -328,6 +332,7 @@ filegroup(
328332
"//self_dev_dependency/mycrate",
329333
"@binaries//:protoc-gen-prost-extra",
330334
"@binaries//:protoc-gen-prost__protoc-gen-prost",
335+
"@binary_only//:b3sum__b3sum",
331336
] + select({
332337
"@platforms//os:windows": [],
333338
"//conditions:default": [

test/MODULE.bazel

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ TESTS = [
158158
"workspace_hyphen_dep_aliases",
159159
"workspace_renamed_path_dep_aliases",
160160
"binaries",
161+
"binary_only",
161162
"uv",
162163
]
163164

@@ -223,6 +224,13 @@ crate.annotation(
223224
gen_binaries = ["protoc-gen-prost"],
224225
)
225226

227+
# Test gen_binaries for a binary-only crate (no [lib], no src/lib.rs).
228+
crate.annotation(
229+
crate = "b3sum",
230+
gen_binaries = ["b3sum"],
231+
repositories = ["binary_only"],
232+
)
233+
226234
# Fix readme inclusions
227235
crate.annotation(
228236
crate = "windows-link",

0 commit comments

Comments
 (0)