Skip to content

Commit e88d6aa

Browse files
committed
Update rust_bindgen_library to only link cc_lib into the direct consumer
1 parent e989bd9 commit e88d6aa

File tree

14 files changed

+240
-18
lines changed

14 files changed

+240
-18
lines changed

bindgen/private/BUILD.bazel

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
11
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
2+
load("//rust:defs.bzl", "rust_binary")
3+
load(":bindgen_utils.bzl", "empty_out_dir")
4+
5+
empty_out_dir(
6+
name = "out_dir",
7+
visibility = ["//visibility:public"],
8+
)
9+
10+
rust_binary(
11+
name = "empty_dir_maker",
12+
srcs = ["empty_dir_maker.rs"],
13+
edition = "2021",
14+
visibility = ["//visibility:public"],
15+
)
216

317
bzl_library(
418
name = "bzl_lib",

bindgen/private/bindgen.bzl

Lines changed: 110 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ load(
2020
)
2121
load("@rules_cc//cc:defs.bzl", "CcInfo")
2222
load("//rust:defs.bzl", "rust_library")
23+
load("//rust:rust_common.bzl", "BuildInfo")
2324

2425
# buildifier: disable=bzl-visibility
2526
load("//rust/private:rustc.bzl", "get_linker_and_args")
@@ -64,27 +65,109 @@ def rust_bindgen_library(
6465
if "tags" in kwargs:
6566
kwargs.pop("tags")
6667

68+
sub_tags = tags + ([] if "manual" in tags else ["manual"])
69+
6770
deps = kwargs.get("deps") or []
6871
if "deps" in kwargs:
6972
kwargs.pop("deps")
7073

74+
bindgen_kwargs = {}
75+
if "linkstatic" in kwargs:
76+
bindgen_kwargs.update({"linkstatic": kwargs.pop("linkstatic")})
77+
7178
rust_bindgen(
7279
name = name + "__bindgen",
7380
header = header,
7481
cc_lib = cc_lib,
7582
bindgen_flags = bindgen_flags or [],
7683
clang_flags = clang_flags or [],
77-
tags = ["manual"],
84+
tags = sub_tags,
85+
**bindgen_kwargs
86+
)
87+
88+
compile_data = kwargs.pop("compile_data", []) + [name + "__bindgen.compile_data"]
89+
native.filegroup(
90+
name = name + "__bindgen.compile_data",
91+
srcs = [name + "__bindgen"],
92+
output_group = "bindgen_compile_data",
93+
tags = sub_tags,
7894
)
7995

96+
for custom_tag in ["__bindgen", "no-clippy", "no-rustfmt"]:
97+
tags = tags + ([] if custom_tag in tags else [custom_tag])
98+
8099
rust_library(
81100
name = name,
82101
srcs = [name + "__bindgen.rs"],
83-
tags = tags + ["__bindgen", "noclippy"],
84-
deps = deps + [cc_lib],
102+
deps = deps + [name + "__bindgen"],
103+
compile_data = compile_data,
104+
tags = tags,
85105
**kwargs
86106
)
87107

108+
def _generate_cc_link_build_info(ctx, cc_lib):
109+
"""Produce the eqivilant cargo_build_script providers for use in linking the library.
110+
111+
Args:
112+
ctx (ctx): The rule's context object
113+
cc_lib (Target): The `rust_bindgen.cc_lib` target.
114+
115+
Returns:
116+
The `BuildInfo` provider.
117+
"""
118+
compile_data = []
119+
linker_flags = []
120+
linker_search_paths = []
121+
122+
for linker_input in cc_lib[CcInfo].linking_context.linker_inputs.to_list():
123+
for lib in linker_input.libraries:
124+
if lib.static_library:
125+
linker_flags.append("-lstatic={}".format(lib.static_library.owner.name))
126+
linker_search_paths.append(lib.static_library.dirname)
127+
compile_data.append(lib.static_library)
128+
elif lib.pic_static_library:
129+
linker_flags.append("-lstatic={}".format(lib.pic_static_library.owner.name))
130+
linker_search_paths.append(lib.pic_static_library.dirname)
131+
compile_data.append(lib.pic_static_library)
132+
133+
linker_flags.extend(linker_input.user_link_flags)
134+
135+
if not compile_data:
136+
fail("No static libraries found in {}".format(
137+
cc_lib.label,
138+
))
139+
140+
empty_file = ctx.actions.declare_file("{}.empty".format(ctx.label.name))
141+
ctx.actions.write(
142+
output = empty_file,
143+
content = "",
144+
)
145+
146+
link_flags = ctx.actions.declare_file("{}.link_flags".format(ctx.label.name))
147+
ctx.actions.write(
148+
output = link_flags,
149+
content = "\n".join(linker_flags),
150+
)
151+
152+
link_search_paths = ctx.actions.declare_file("{}.link_search_paths".format(ctx.label.name))
153+
ctx.actions.write(
154+
output = link_search_paths,
155+
content = "\n".join([
156+
"-Lnative=${{pwd}}/{}".format(path)
157+
for path in depset(linker_search_paths).to_list()
158+
]),
159+
)
160+
161+
return BuildInfo(
162+
out_dir = ctx.file._bindgen_out_dir,
163+
rustc_env = empty_file,
164+
dep_env = empty_file,
165+
flags = empty_file,
166+
link_flags = link_flags,
167+
link_search_paths = link_search_paths,
168+
compile_data = depset(compile_data),
169+
)
170+
88171
def _rust_bindgen_impl(ctx):
89172
# nb. We can't grab the cc_library`s direct headers, so a header must be provided.
90173
cc_lib = ctx.attr.cc_lib
@@ -200,6 +283,19 @@ def _rust_bindgen_impl(ctx):
200283
tools = tools,
201284
)
202285

286+
if ctx.attr.linkstatic:
287+
providers = [_generate_cc_link_build_info(ctx, cc_lib)]
288+
else:
289+
providers = [cc_common.merge_cc_infos(
290+
direct_cc_infos = [cc_lib[CcInfo]],
291+
)]
292+
293+
return providers + [
294+
OutputGroupInfo(
295+
bindgen_bindings = depset([output]),
296+
),
297+
]
298+
203299
rust_bindgen = rule(
204300
doc = "Generates a rust source file from a cc_library and a header.",
205301
implementation = _rust_bindgen_impl,
@@ -220,6 +316,17 @@ rust_bindgen = rule(
220316
allow_single_file = True,
221317
mandatory = True,
222318
),
319+
"linkstatic": attr.bool(
320+
doc = (
321+
"If True, `cc_lib` will be statically linked directly into `rust_library that consumes it. " +
322+
"This will stop the propagation of the `CcInfo` provider from `cc_lib` into downstream targets. "
323+
),
324+
default = False,
325+
),
326+
"_bindgen_out_dir": attr.label(
327+
default = Label("//bindgen/private:out_dir"),
328+
allow_single_file = True,
329+
),
223330
"_cc_toolchain": attr.label(
224331
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
225332
),

bindgen/private/bindgen_utils.bzl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""Rust Bindgen utilities"""
2+
3+
def _empty_out_dir_impl(ctx):
4+
output = ctx.actions.declare_directory(ctx.label.name)
5+
6+
args = ctx.actions.args()
7+
args.add(output.path)
8+
9+
ctx.actions.run(
10+
mnemonic = "RustBindgenOutDir",
11+
executable = ctx.executable._generator,
12+
arguments = [args],
13+
outputs = [output],
14+
)
15+
16+
return [DefaultInfo(files = depset([output]))]
17+
18+
empty_out_dir = rule(
19+
doc = "A rule which creates an empty directory.",
20+
implementation = _empty_out_dir_impl,
21+
attrs = {
22+
"_generator": attr.label(
23+
cfg = "exec",
24+
executable = True,
25+
default = Label("//bindgen/private:empty_dir_maker"),
26+
),
27+
},
28+
)

bindgen/private/empty_dir_maker.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//! A utility binary for creating directories.
2+
3+
use std::env;
4+
use std::fs;
5+
6+
fn main() {
7+
let dir = env::args().last().expect("No arguments passed");
8+
fs::create_dir_all(dir).expect("Failed to create directory");
9+
}

cargo/private/cargo_build_script.bzl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ def _cargo_build_script_impl(ctx):
269269
flags = flags_out,
270270
link_flags = link_flags,
271271
link_search_paths = link_search_paths,
272+
compile_data = depset([]),
272273
),
273274
OutputGroupInfo(
274275
streams = depset([streams.stdout, streams.stderr]),
@@ -417,6 +418,7 @@ def _cargo_dep_env_implementation(ctx):
417418
link_search_paths = empty_file,
418419
out_dir = out_dir,
419420
rustc_env = empty_file,
421+
compile_data = depset([]),
420422
))
421423
return [
422424
DefaultInfo(files = depset(ctx.files.src)),
@@ -435,6 +437,7 @@ def _cargo_dep_env_implementation(ctx):
435437
link_search_paths = empty_file,
436438
out_dir = empty_dir,
437439
rustc_env = empty_file,
440+
compile_data = depset([]),
438441
),
439442
# Information here is used directly by dependencies, and it is an error to have more than
440443
# one dependency which sets this. This is the main way to specify information from build

examples/bindgen/BUILD.bazel

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
1-
load("@rules_cc//cc:defs.bzl", "cc_library")
21
load("@rules_rust//bindgen:bindgen.bzl", "rust_bindgen_library")
32
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_test")
43

5-
cc_library(
6-
name = "simple",
7-
srcs = ["simple.h"],
8-
)
9-
104
rust_bindgen_library(
115
name = "simple_bindgen",
126
bindgen_flags = [
7+
"--allowlist-function=simple_.*",
138
"--allowlist-var=SIMPLE_.*",
149
],
15-
cc_lib = ":simple",
16-
header = "simple.h",
10+
cc_lib = "//bindgen/simple",
11+
header = "//bindgen/simple:simple.h",
12+
linkstatic = True,
1713
)
1814

1915
rust_binary(

examples/bindgen/main.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1+
use simple_bindgen;
2+
3+
fn simple_function() -> i64 {
4+
unsafe { return simple_bindgen::simple_function() }
5+
}
6+
17
fn main() {
2-
println!("The value is {}!", simple_bindgen::SIMPLE_VALUE);
8+
println!(
9+
"The values are {} and {}!",
10+
simple_bindgen::SIMPLE_VALUE,
11+
simple_function()
12+
);
313
}
414

515
#[cfg(test)]
616
mod test {
717
#[test]
818
fn do_the_test() {
919
assert_eq!(42, simple_bindgen::SIMPLE_VALUE);
20+
assert_eq!(1337, super::simple_function());
1021
}
1122
}

examples/bindgen/main_shared.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use simple_shared_bindgen;
2+
3+
fn simple_function() -> i64 {
4+
unsafe { return simple_shared_bindgen::simple_function() }
5+
}
6+
7+
fn main() {
8+
println!(
9+
"The values are {} and {}!",
10+
simple_shared_bindgen::SIMPLE_VALUE,
11+
simple_function()
12+
);
13+
}
14+
15+
#[cfg(test)]
16+
mod test {
17+
#[test]
18+
fn do_the_test() {
19+
assert_eq!(42, simple_shared_bindgen::SIMPLE_VALUE);
20+
assert_eq!(1337, super::simple_function());
21+
}
22+
}

examples/bindgen/simple.h

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
load("@rules_cc//cc:defs.bzl", "cc_library")
2+
3+
exports_files(
4+
[
5+
"simple.h",
6+
],
7+
visibility = ["//bindgen:__pkg__"],
8+
)
9+
10+
cc_library(
11+
name = "simple",
12+
srcs = ["simple.cc"],
13+
hdrs = ["simple.h"],
14+
visibility = ["//bindgen:__pkg__"],
15+
)

0 commit comments

Comments
 (0)