Skip to content

Commit ae1be23

Browse files
authored
cargo-bazel: fix binary targets of proc-macro crates using wrong dep attribute (#3975)
## Problem When a crate has both a `rust_proc_macro` library target and `rust_binary` targets (e.g. development utilities shipped alongside the proc-macro), `make_rust_binary` unconditionally places the library in `deps`. However, rules_rust validates that proc-macro libraries must appear in `proc_macro_deps`, not `deps`. This causes analysis failures for any such crate. ## Solution In `make_rust_binary`, detect whether the crate's library target is a proc-macro by checking if `Rule::ProcMacro(_)` is present in `krate.targets`. If it is, insert the library label into `proc_macro_deps` instead of `deps`. ## Testing - `binary_of_proc_macro_crate_uses_proc_macro_deps`: verifies that the generated `rust_binary` has the `proc-macr`o lib in `proc_macro_deps` and not in `deps`. --------- Signed-off-by: Thomas Lam <thomaslam@canva.com>
1 parent 85007f4 commit ae1be23

1 file changed

Lines changed: 94 additions & 20 deletions

File tree

crate_universe/src/rendering.rs

Lines changed: 94 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -735,28 +735,35 @@ impl Renderer {
735735
krate: &CrateContext,
736736
target: &TargetAttributes,
737737
) -> Result<RustBinary> {
738+
// If the crate's library target is a proc-macro, the binary must list
739+
// it in `proc_macro_deps` rather than `deps`; rules_rust validates this.
740+
let lib_is_proc_macro = krate
741+
.targets
742+
.iter()
743+
.any(|rule| matches!(rule, Rule::ProcMacro(_)));
744+
745+
let mut deps = self.make_deps(
746+
krate.common_attrs.deps.clone(),
747+
krate.common_attrs.extra_deps.clone(),
748+
);
749+
let mut proc_macro_deps = self.make_deps(
750+
krate.common_attrs.proc_macro_deps.clone(),
751+
krate.common_attrs.extra_proc_macro_deps.clone(),
752+
);
753+
754+
if let Some(library_target_name) = &krate.library_target_name {
755+
let lib_label = Label::from_str(&format!(":{library_target_name}")).unwrap();
756+
if lib_is_proc_macro {
757+
proc_macro_deps.insert(lib_label, None);
758+
} else {
759+
deps.insert(lib_label, None);
760+
}
761+
}
762+
738763
Ok(RustBinary {
739764
name: format!("{}__bin", target.crate_name),
740-
deps: {
741-
let mut deps = self.make_deps(
742-
krate.common_attrs.deps.clone(),
743-
krate.common_attrs.extra_deps.clone(),
744-
);
745-
if let Some(library_target_name) = &krate.library_target_name {
746-
deps.insert(
747-
Label::from_str(&format!(":{library_target_name}")).unwrap(),
748-
None,
749-
);
750-
}
751-
SelectSet::new(deps, platforms)
752-
},
753-
proc_macro_deps: SelectSet::new(
754-
self.make_deps(
755-
krate.common_attrs.proc_macro_deps.clone(),
756-
krate.common_attrs.extra_proc_macro_deps.clone(),
757-
),
758-
platforms,
759-
),
765+
deps: SelectSet::new(deps, platforms),
766+
proc_macro_deps: SelectSet::new(proc_macro_deps, platforms),
760767
aliases: SelectDict::new(self.make_aliases(krate, false, false), platforms),
761768
common: self.make_common_attrs(platforms, krate, target)?,
762769
})
@@ -2276,4 +2283,71 @@ mod test {
22762283
.replace(' ', "")
22772284
.contains(&expected.replace(' ', "")));
22782285
}
2286+
2287+
/// Binary targets of a proc-macro crate must list the proc-macro lib in
2288+
/// `proc_macro_deps`, not `deps`. rules_rust validates this strictly.
2289+
#[test]
2290+
fn binary_of_proc_macro_crate_uses_proc_macro_deps() {
2291+
let mut context = Context::default();
2292+
let crate_id = CrateId::new("my_proc_macro".to_owned(), VERSION_ZERO_ONE_ZERO);
2293+
context.crates.insert(
2294+
crate_id.clone(),
2295+
CrateContext {
2296+
name: crate_id.name.clone(),
2297+
version: crate_id.version.clone(),
2298+
package_url: None,
2299+
targets: BTreeSet::from([
2300+
Rule::ProcMacro(TargetAttributes {
2301+
crate_name: "my_proc_macro".to_owned(),
2302+
crate_root: Some("src/lib.rs".to_owned()),
2303+
..TargetAttributes::default()
2304+
}),
2305+
Rule::Binary(TargetAttributes {
2306+
crate_name: "my_bin".to_owned(),
2307+
crate_root: Some("src/main.rs".to_owned()),
2308+
..TargetAttributes::default()
2309+
}),
2310+
]),
2311+
library_target_name: Some("my_proc_macro".to_owned()),
2312+
common_attrs: CommonAttributes::default(),
2313+
build_script_attrs: None,
2314+
repository: None,
2315+
license: None,
2316+
license_ids: BTreeSet::default(),
2317+
license_file: None,
2318+
additive_build_file_content: None,
2319+
disable_pipelining: false,
2320+
extra_aliased_targets: BTreeMap::default(),
2321+
alias_rule: None,
2322+
override_targets: BTreeMap::default(),
2323+
},
2324+
);
2325+
2326+
let renderer = Renderer::new(mock_render_config(None), mock_supported_platform_triples());
2327+
let output = renderer.render(&context, None).unwrap();
2328+
let build_file_content = output
2329+
.get(&PathBuf::from("BUILD.my_proc_macro-0.1.0.bazel"))
2330+
.unwrap();
2331+
2332+
// Find the rust_binary section.
2333+
let binary_start = build_file_content
2334+
.find("rust_binary(")
2335+
.expect("should contain rust_binary");
2336+
let binary_section = &build_file_content[binary_start..];
2337+
2338+
// When the crate's lib is a proc-macro, deps will be empty and therefore
2339+
// omitted from the rendered output. Only proc_macro_deps should be present.
2340+
let proc_macro_deps_pos = binary_section
2341+
.find(" proc_macro_deps = ")
2342+
.expect("binary should have proc_macro_deps attribute");
2343+
2344+
assert!(
2345+
!binary_section[..proc_macro_deps_pos].contains(":my_proc_macro"),
2346+
"proc-macro lib must not appear before proc_macro_deps:\n{binary_section}"
2347+
);
2348+
assert!(
2349+
binary_section[proc_macro_deps_pos..].contains(":my_proc_macro"),
2350+
"proc-macro lib must appear in proc_macro_deps:\n{binary_section}"
2351+
);
2352+
}
22792353
}

0 commit comments

Comments
 (0)