Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Rules used to compile Rust code.
* `rust_binary`
* `rust_test`
* `rust_static_library`
* `rust_dylib_library`
* `rust_shared_library`
* `rust_proc_macro`
* `cargo_build_script`
Expand Down
18 changes: 18 additions & 0 deletions docs/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,22 @@ stardoc(
deps = [":all_docs"],
)

stardoc(
name = "rust_cdylib_library",
out = "src/rust_cdylib_library.md",
input = "@rules_rust//rust:rust_cdylib_library.bzl",
symbol_names = ["rust_cdylib_library"],
deps = [":all_docs"],
)

stardoc(
name = "rust_dylib_library",
out = "src/rust_dylib_library.md",
input = "@rules_rust//rust:rust_dylib_library.bzl",
symbol_names = ["rust_dylib_library"],
deps = [":all_docs"],
)

stardoc(
name = "rust_shared_library",
out = "src/rust_shared_library.md",
Expand Down Expand Up @@ -345,12 +361,14 @@ mdbook(
":rust_analyzer_toolchain",
":rust_binary",
":rust_bindgen",
":rust_cdylib_library",
":rust_bzlmod",
":rust_clippy",
":rust_clippy_aspect",
":rust_clippy_test",
":rust_doc",
":rust_doc_test",
":rust_dylib_library",
":rust_library",
":rust_library_group",
":rust_lint_config",
Expand Down
2 changes: 2 additions & 0 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
- [rust_library](./rust_library.md)
- [rust_library_group](./rust_library_group.md)
- [rust_lint_config](./rust_lint_config.md)
- [rust_cdylib_library](./rust_cdylib_library.md)
- [rust_dylib_library](./rust_dylib_library.md)
- [rust_proc_macro](./rust_proc_macro.md)
- [rust_shared_library](./rust_shared_library.md)
- [rust_static_library](./rust_static_library.md)
Expand Down
6 changes: 5 additions & 1 deletion docs/src/rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@ directly to the crate types Cargo produces.
other Rust targets can depend on.
- [rust_static_library](./rust_static_library.md) — Build a `staticlib`
(`--crate-type=staticlib`) for linking Rust code into a C/C++ binary.
- [rust_shared_library](./rust_shared_library.md) — Build a `cdylib`
- [rust_cdylib_library](./rust_cdylib_library.md) — Build a `cdylib`
(`--crate-type=cdylib`) for use as a shared library from C/C++ or other
languages.
- [rust_dylib_library](./rust_dylib_library.md) — Build a `dylib`
(`--crate-type=dylib`) for use as a shared library with the unstable Rust ABI.
- [rust_shared_library](./rust_shared_library.md) — Convenience alias over
`rust_cdylib_library`.
- [rust_proc_macro](./rust_proc_macro.md) — Build a procedural macro crate
(`--crate-type=proc-macro`) that other Rust targets can consume as a compile-time
plugin.
Expand Down
8 changes: 6 additions & 2 deletions rust/defs.bzl
Comment thread
UebelAndre marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,11 @@ load("//rust/private:common.bzl", _rust_common = "rust_common")
load(
"//rust/private:rust.bzl",
_rust_binary = "rust_binary",
_rust_cdylib_library = "rust_cdylib_library",
_rust_dylib_library = "rust_dylib_library",
_rust_library = "rust_library",
_rust_library_group = "rust_library_group",
_rust_proc_macro = "rust_proc_macro",
_rust_shared_library = "rust_shared_library",
_rust_static_library = "rust_static_library",
_rust_test = "rust_test",
)
Expand All @@ -103,7 +104,10 @@ rust_library = _rust_library
rust_static_library = _rust_static_library
# See @rules_rust//rust/private:rust.bzl for a complete description.

rust_shared_library = _rust_shared_library
rust_dylib_library = _rust_dylib_library
# See @rules_rust//rust/private:rust.bzl for a complete description.

rust_shared_library = _rust_cdylib_library
# See @rules_rust//rust/private:rust.bzl for a complete description.

rust_proc_macro = _rust_proc_macro
Expand Down
2 changes: 2 additions & 0 deletions rust/private/providers.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ AllocatorLibrariesInfo = provider(
"global_allocator_library": "Optional[CcInfo]: used when a global rust allocator is used",
"libstd_and_allocator_ccinfo": "Optional[CcInfo]: used when the default rust allocator is used",
"libstd_and_global_allocator_ccinfo": "Optional[CcInfo]: used when a global rust allocator is used",
"libstd_dylib_and_allocator_ccinfo": "Optional[CcInfo]: used when the default rust allocator is used with a dylib stdlib",
"libstd_dylib_and_global_allocator_ccinfo": "Optional[CcInfo]: used when a global rust allocator is used with a dylib stdlib",
"nostd_and_global_allocator_ccinfo": "Optional[CcInfo]: used when nostd with a global rust allocator is used",
},
)
Expand Down
96 changes: 91 additions & 5 deletions rust/private/rust.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,29 @@ def _rust_static_library_impl(ctx):
"""
return _rust_library_common(ctx, "staticlib")

def _rust_shared_library_impl(ctx):
"""The implementation of the `rust_shared_library` rule.
def _rust_dylib_library_impl(ctx):
"""The implementation of the `rust_dylib_library` rule.

This rule provides CcInfo, so it can be used everywhere Bazel expects
rules_cc.

**Note**: When dynamic libraries are listed as dependencies for other Rust
binaries they can induce errors from multiply defined symbols, causing
linker errors in rustc. Some libraries in the dependency graph may need to
be converted to dynamic libraries, and/or have the standard library
dynamically linked (`link_std_dylib`) to avoid this. These rules do not
attempt to resolve these linking issues automatically.

Args:
ctx (ctx): The rule's context object

Returns:
list: A list of providers.
"""
return _rust_library_common(ctx, "dylib")

def _rust_cdylib_library_impl(ctx):
"""The implementation of the `rust_cdylib_library` rule.

This rule provides CcInfo, so it can be used everywhere Bazel
expects rules_cc.
Expand Down Expand Up @@ -987,6 +1008,15 @@ _RUST_TEST_ATTRS = {
"env_inherit": attr.string_list(
doc = "Specifies additional environment variables to inherit from the external environment when the test is executed by bazel test.",
),
"link_std_dylib": attr.bool(
mandatory = False,
default = False,
doc = dedent("""\
Flag to dynamically link the standard library as a Rust dylib .so object when building this test.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't we also need this for rust_binary?

@buntonj buntonj Aug 24, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, it's there!


Default is false. This is often required when testing a target that depends on a Rust ABI dylib.
"""),
),
"use_libtest_harness": attr.bool(
mandatory = False,
default = True,
Expand Down Expand Up @@ -1082,6 +1112,53 @@ rust_library = rule(
"""),
)

rust_dylib_library = rule(
implementation = _rust_dylib_library_impl,
provides = COMMON_PROVIDERS,
attrs = _COMMON_ATTRS | {
"disable_pipelining": attr.bool(
default = False,
doc = dedent("""\
Disables pipelining for this rule if it is globally enabled.
This will cause this rule to not produce a `.rmeta` file and all the dependent
crates will instead use the `.rlib` file.
"""),
),
"link_std_dylib": attr.bool(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have use cases at all for rust_dylib_library where we don't wanna link the stdlibs dynamically? Naively, for this rule the default should be true?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's totally fair. AFAICT, Rust ABI dylibs will just be depended on by Rust targets, which would require link_std_dylib anywho, so setting the default to true is harmless. Done!

mandatory = False,
default = True,
doc = dedent("""\
Flag to dynamically link the standard library as a Rust dylib .so object when building this library.

Default is true. This is typically required for Rust ABI dylibs so that the stdlib is shared
with the binary that loads them, avoiding duplicate symbols.
"""),
),
},
fragments = ["cpp"],
toolchains = [
str(Label("//rust:toolchain_type")),
config_common.toolchain_type("@bazel_tools//tools/cpp:toolchain_type", mandatory = False),
],
doc = dedent("""\
Builds a shared library using the unstable Rust ABI.

This library can be depended on by other Rust targets via --extern,
making it suitable for splitting a Rust project into separately compiled
dynamic libraries. Note that the Rust ABI is not stable across compiler
versions.

This rule provides CcInfo, so it can be used everywhere Bazel expects `rules_cc`.

**Note**: When dynamic libraries are listed as dependencies for other Rust
binaries they can induce errors from multiply defined symbols, causing
linker errors in rustc. Some libraries in the dependency graph may need to
be converted to dynamic libraries, and/or have the standard library
dynamically linked (`link_std_dylib`) to avoid this. These rules do not
attempt to resolve these linking issues automatically.
"""),
)

def _resolve_platform(settings, attr):
"""Resolve the platform label for a transition, adding @ prefix if needed.

Expand Down Expand Up @@ -1158,8 +1235,8 @@ _rust_shared_library_transition = transition(
],
)

rust_shared_library = rule(
implementation = _rust_shared_library_impl,
rust_cdylib_library = rule(
implementation = _rust_cdylib_library_impl,
attrs = _COMMON_ATTRS | _PLATFORM_ATTRS | _EXPERIMENTAL_USE_CC_COMMON_LINK_ATTRS,
fragments = ["cpp"],
cfg = _rust_shared_library_transition,
Expand All @@ -1172,7 +1249,7 @@ rust_shared_library = rule(
rust_common.test_crate_info,
],
doc = dedent("""\
Builds a Rust shared library.
Builds a C ABI Rust shared library.

This shared library will contain all transitively reachable crates and native objects.
It is meant to be used when producing an artifact that is then consumed by some other build system
Expand Down Expand Up @@ -1226,6 +1303,15 @@ _RUST_BINARY_ATTRS = {
more complicated debugger attachment.
"""),
),
"link_std_dylib": attr.bool(
mandatory = False,
default = False,
doc = dedent("""\
Flag to dynamically link the standard library as a Rust dylib .so object when building this binary.

Default is false. This is often required when building a binary that depends on a Rust ABI dylib.
"""),
),
"linker_script": attr.label(
doc = dedent("""\
Link script to forward into linker via rustc options.
Expand Down
20 changes: 13 additions & 7 deletions rust/private/rust_allocator_libraries.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def make_libstd_and_allocator_ccinfo(
feature_configuration,
label,
actions,
experimental_link_std_dylib,
link_std_dylib,
rust_std,
allocator_library,
std = "std"):
Expand All @@ -54,7 +54,7 @@ def make_libstd_and_allocator_ccinfo(
feature_configuration (feature_configuration): feature_configuration to be queried.
label (Label): The rule's label.
actions: The rule's ctx.actions object.
experimental_link_std_dylib (boolean): The value of the standard library's `_experimental_link_std_dylib(ctx)`.
link_std_dylib (boolean): If the standard library should be included as a dylib.
rust_std: The Rust standard library.
allocator_library (struct): The target to use for providing allocator functions.
This should be a struct with either:
Expand Down Expand Up @@ -82,6 +82,9 @@ def make_libstd_and_allocator_ccinfo(
""").format(label, rust_std))
rust_stdlib_info = rust_std[rust_common.stdlib_info]

if link_std_dylib and (not rust_stdlib_info.std_dylib or not cc_toolchain):
return None

if rust_stdlib_info.self_contained_files:
compilation_outputs = cc_common.create_compilation_outputs(
objects = depset(rust_stdlib_info.self_contained_files),
Expand Down Expand Up @@ -183,7 +186,7 @@ def make_libstd_and_allocator_ccinfo(
order = "topological",
)

if experimental_link_std_dylib:
if link_std_dylib:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this flag no longer experimental now?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, less that it's not experimental and more that it's now a computed quantity/property as a function of the toolchain-level experimental_link_std_dylib and the target-level link_std_dylib.

I opted to try and rename things so that the experimental_link_std_dylib hook and name are the same, but all internal customers/byproducts of the new link_std_dylib are not experimental_ anymore.

# std dylib has everything so that we do not need to include all std_files
std_inputs = depset(
[cc_common.create_library_to_link(
Expand Down Expand Up @@ -264,20 +267,23 @@ def _rust_allocator_libraries_impl(ctx):

toolchain = find_toolchain(ctx)

def make_cc_info(info, std):
def make_cc_info(info, std, link_std_dylib):
return toolchain.make_libstd_and_allocator_ccinfo(
ctx.label,
ctx.actions,
struct(allocator_libraries_impl_info = info),
std,
link_std_dylib,
)

providers = [AllocatorLibrariesInfo(
allocator_library = allocator_library,
global_allocator_library = global_allocator_library,
libstd_and_allocator_ccinfo = make_cc_info(allocator_library, "std"),
libstd_and_global_allocator_ccinfo = make_cc_info(global_allocator_library, "std"),
nostd_and_global_allocator_ccinfo = make_cc_info(global_allocator_library, "no_std_with_alloc"),
libstd_and_allocator_ccinfo = make_cc_info(allocator_library, "std", False),
libstd_and_global_allocator_ccinfo = make_cc_info(global_allocator_library, "std", False),
nostd_and_global_allocator_ccinfo = make_cc_info(global_allocator_library, "no_std_with_alloc", False),
libstd_dylib_and_allocator_ccinfo = make_cc_info(allocator_library, "std", True),
libstd_dylib_and_global_allocator_ccinfo = make_cc_info(global_allocator_library, "std", True),
)]

return providers
Expand Down
Loading