Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes for rust_binary that depends on a cc_library that depends on a rust_library #825

Merged
merged 18 commits into from
Jul 20, 2021

Conversation

krasimirgg
Copy link
Collaborator

@krasimirgg krasimirgg commented Jul 9, 2021

Consider a case of a rust_binary that depends on a cc_library that itself depends
on a rust_library:

.
├── bin.rs
├── BUILD
└── lib.rs
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library")

rust_library(
    name = "rust_lib",
    srcs = ["lib.rs"],
)

cc_library(
    name = "cc_lib",
    srcs = [],
    deps = [":rust_lib"],
)

rust_binary(
    name = "bin",
    srcs = ["bin.rs"],
    deps = [":cc_lib"],
)
// bin.rs
fn main() {
    println!("hi");
}
// lib.rs: empty

Running bazel build //project:bin runs into several linker errors, which this PR tries to address:

  • When passing an argument like -lcrate, the linker looks for a static library named libcrate.a.
  • the CC link actions get confused by static library names that have multiple dots, e.g., end in "libcrate.rlib.a", so
    updated the scripts to produce them as "libcrate-rlib.a" instead.
  • The libraries panic_abort and panic_unwind are alternatives; attempting to pass both to the linker runs into duplicate symbol errors. Updated the rules to prefer panic_unwind if both are available. This is since the standard library by default depends on panic_unwind. Note that in platforms where panic_unwind is not available, panic_abort will be picked. In the future we could add a feature that allows users to pick the panic strategy, aligned with how it will be done in upstream rustc (Support different panic strategies. rust-lang/wg-cargo-std-aware#29).

Another thing worth noting is that the way we currently process .rlib-s into static libs to pass on to CC rules doesn't quite work when using the current stable rustc 1.53.0; it works for the beta rustc and beyond: on Linux, a crate.rlib is an archive that contains .o files (good) and a Rust metadata file lib.rmeta (caution). We symlink crate.rlib into libcrate.a. The linker generally checks that all members of an archive passed in a --whole-archive section are ELF files. The commit rust-lang/rust@c79419a on Jun 4 updated rustc so that the produced lib.rmeta is an ELF file (previously it was a binary blob), which satisfies the linker checks. This PR does not attempt to address the case wherelib.rmeta is not an ELF file, hence even with this PR in, trying to run the example with the current stable rustc 1.53.0 will produce linker errors complaining that an archive member is not recognized, like this one /usr/bin/ld.gold: error: rust_lib: plugin failed to claim member lib.rmeta at 2480.

With this PR, bazel build //project:bin works (with sufficiently recent rustc, as described above).

Fixes static library naming issues that cause the build of a rust_binary that
depends on a cc_library that itself depends on a rust_library to fail.

Consider a rust_binary that depends on a cc_library that itself depends
on a rust_library:

```
.
├── bin.rs
├── BUILD
└── lib.rs
```

```rust
// bin.rs
fn main() {
    println!("hi");
}
```

```rust
// lib.rs: empty
```

```bazel
load("@rules_rust//rust:defs.bzl", "rust_benchmark", "rust_binary", "rust_library")

rust_library(
    name = "rust_lib",
    srcs = ["lib.rs"],
)

cc_library(
    name = "cc_lib",
    srcs = [],
    deps = [":rust_lib"],
)

rust_binary(
    name = "bin",
    srcs = ["bin.rs"],
    deps = [":cc_lib"],
)
```

Running `bazel build //project:bin` runs into linker errors like:

```
/usr/bin/ld.gold: error: cannot find -lrust_lib
/usr/bin/ld.gold: error: cannot find -lrustc_std_workspace_alloc-1ff59d4f23b10626
```

This is because the linker expects the static library corresponding to `-lname`
to be named `libname.a`. Also Bazel CC link actions get confused by static
library names that have multiple dots, e.g., end in "libcrate.rlib.a", so
updated the scripts to produce them as "libcrate-rlib.a" instead.
Fixes an issue that causes the build of a rust_binary that depends on a
cc_library that depends on a rust_library to fail.

Consider a rust_binary that depends on a cc_library that itself depends
on a rust_library:

```
.
├── bin.rs
├── BUILD
└── lib.rs
```

```rust
// bin.rs
fn main() {
    println!("hi");
}
```

```rust
// lib.rs: empty
```

```bazel
load("@rules_rust//rust:defs.bzl", "rust_benchmark", "rust_binary", "rust_library")

rust_library(
    name = "rust_lib",
    srcs = ["lib.rs"],
)

cc_library(
    name = "cc_lib",
    srcs = [],
    deps = [":rust_lib"],
)

rust_binary(
    name = "bin",
    srcs = ["bin.rs"],
    deps = [":cc_lib"],
)
```

Running `bazel build //project:bin` runs into linker errors like:

/usr/bin/ld.gold: error: bazel-out/k8-fastbuild/bin/external/rust_linux_x86_64/lib/rustlib/x86_64-unknown-linux-gnu/lib/libpanic_unwind-5f5ff14665a8d5c5-rlib.a(panic_unwind-5f5ff14665a8d5c5.panic_unwind.p9ngf95o-cgu.0.rcgu.o): multiple definition of '__rust_panic_cleanup'
          /usr/bin/ld.gold: bazel-out/k8-fastbuild/bin/external/rust_linux_x86_64/lib/rustlib/x86_64-unknown-linux-gnu/lib/libpanic_abort-c6166278e71d9daf-rlib.a(panic_abort-c6166278e71d9daf.panic_abort.4p8k2zko-cgu.0.rcgu.o): previous definition here

That's because we're passing both panic_unwind and panic_abort to the linker.
The standard library by default depends on panic_unwind. This updates the rules
to prefer panic_unwind. It would be nice to have an option for the user to
pick the panic runtime similarly to how it could be done upstream:
https://rustc-dev-guide.rust-lang.org/panic-implementation.html#step-2-the-panic-runtime
@google-cla google-cla bot added the cla: yes label Jul 9, 2021
@UebelAndre
Copy link
Collaborator

Hi and thanks for opening this PR! 😄

Could you also add a regression test to the test package?

@krasimirgg
Copy link
Collaborator Author

Could you also add a regression test to the test package?

Added some tests.

Copy link
Collaborator

@UebelAndre UebelAndre left a comment

Choose a reason for hiding this comment

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

Thanks for adding a test!

I think this generally looks pretty good but I'm wondering if the logic for parsing out panic_unwind vs panic_abort could be moved out of rust_stdlib_filegroup and into rust_toolchain (specifically _make_libstd_and_allocator_ccinfo). I think the rust_stdlib_filegroup rule should be gathering everything from a rust_std artifact but not making any decisions on what to or not-to include. I would say, either leave both panic_unwind and panic_abort in std_rlibs (and therefor dot_a_files) and parse them out later, or update the StdLibInfo provider to have panic_unwind_files and panic_abort_files that have the appropriate things parsed out of the various fields there. I think I lean towards the latter but not strongly. How does that sound?

rust/toolchain.bzl Outdated Show resolved Hide resolved
Copy link
Collaborator

@UebelAndre UebelAndre left a comment

Choose a reason for hiding this comment

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

This looks good to me! Thank you! 🙏

I'll give @hlopko a chance to take a look before merging just to be safe 😅

@krasimirgg krasimirgg requested a review from hlopko July 14, 2021 10:58
Copy link
Member

@hlopko hlopko left a comment

Choose a reason for hiding this comment

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

LGTM, thank you! Only minor questions.

rust/private/rustc.bzl Outdated Show resolved Hide resolved
rust/toolchain.bzl Outdated Show resolved Hide resolved
rust/private/utils.bzl Outdated Show resolved Hide resolved
Copy link
Member

@hlopko hlopko left a comment

Choose a reason for hiding this comment

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

Only nits, thank you!

rust/private/rustc.bzl Outdated Show resolved Hide resolved
rust/private/utils.bzl Outdated Show resolved Hide resolved
rust/private/utils.bzl Outdated Show resolved Hide resolved
rust/toolchain.bzl Outdated Show resolved Hide resolved
rust/toolchain.bzl Outdated Show resolved Hide resolved
test/unit/stdlib/stdlib.bzl Outdated Show resolved Hide resolved
@hlopko hlopko merged commit 5ec77de into bazelbuild:main Jul 20, 2021
@krasimirgg krasimirgg deleted the rust_cc_rust branch July 20, 2021 10:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants