bazelbuild / bazelbuild/rules_rust
linking a `rust_binary` that depends on two `cc_libraries` with the same name fails
- Dominant language
- Starlark
- Stars
- 843
- Forks
- 651
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 15
Description
This depends on https://github.com/bazelbuild/rules_rust/pull/825. In particular, trying out this example without https://github.com/bazelbuild/rules_rust/pull/825 will run into the linker errors described there and this example assumes using at least the beta `rustc`. I'm using Linux with ld.
Consider a case where a `rust_binary` (transitively) depends on two `cc_library`es that have the same name. In the wild targets with common names like `utils`, or `status` could occur in unrelated packages.
Like in this example where `//same_names:rbin` depends on `//same_names/x:exc` and `//same_names/y:exc`:
```
same_names
├── BUILD
├── rbin.rs
├── x
│ ├── BUILD
│ └── exc.cc
└── y
├── BUILD
└── exc.cc
```
```bazel
# same_names/BUILD
load("@rules_rust//rust:defs.bzl", "rust_binary")
rust_binary(
name = "rbin",
srcs = ["rbin.rs"],
deps = [
"//same_names/x:exc",
"//same_names/y:exc",
],
)
```
```rust
// same_names/rbin.rs
use std::os::raw::c_int;
extern "C" {
pub fn cx() -> c_int;
pub fn cy() -> c_int;
}
fn main() {
println!("hi {} {}", unsafe { cx() }, unsafe { cy() });
}
```
```bazel
# same_names/x/BUILD
package(default_visibility = ["//visibility:public"])
cc_library(
name = "exc",
srcs = ["exc.cc"],
)
```
```cpp
// same_names/x/exc.cc
extern "C" int cx() {
return 17;
}
```
```bazel
# same_names/y/BUILD
package(default_visibility = ["//visibility:public"])
cc_library(
name = "exc",
srcs = ["exc.cc"],
)
```
```cpp
// same_names/y/exc.cc
extern "C" int cy() {
return 113;
}
```
Running `bazel build //same_names:rbin` fails with a linker error like:
```
bin::main: error: undefined reference to 'cy'
```
The problem is that the way we construct linker command line arguments for native dependencies doesn't work when they have the same name. For each transitive native dependency, we pass its package directory [as `-Lnative=`](https://github.com/bazelbuild/rules_rust/blob/a814d859845c420fd105c629134c4a4cb47ba3f8/rust/private/rustc.bzl#L897) and its name [as `-lstatic=`](https://github.com/bazelbuild/rules_rust/blob/a814d859845c420fd105c629134c4a4cb47ba3f8/rust/private/rustc.bzl#L840) to `rustc`. These get translated to `-Lpackage` and `-lname` linker arguments. In the example above, the `rustc` arguments are like:
```
...
'-Lnative=bazel-out/k8-fastbuild/bin/same_names/x'
'-Lnative=bazel-out/k8-fastbuild/bin/same_names/y'
'-lstatic=exc'
'-lstatic=exc'
...
```
and get translated as linker arguments like:
```
...
'-Lbazel-out/k8-fastbuild/bin/same_names/x'
'-Lbazel-out/k8-fastbuild/bin/same_names/y'
'-lexc'
'-lexc'
...
```
Now the problem is that the linker always picks up the first `libexc.a` library that it finds on the path, in this case `same_names/x/libexc.a`, and hence the undefined reference errors for the symbol defined in `same_names/y/libexc.a`.
The way cc rules deal with this is that they just pass the full path to the library to the linker, AFAIK. I think this is a good way to address this and potentially some other naming incompatibilities across cc and rust rules.
Under the [native-link-modifiers RFC](https://rust-lang.github.io/rfcs/2951-native-link-modifiers.html#relative-order-of--l-and--clink-args-options), the relative order of `-l` and `-Clink-arg(s)` is preserved, so it should be OK to replace the current linker args pattern:
```
...
'-Lnative=bazel-out/k8-fastbuild/bin/same_names/x'
'-Lnative=bazel-out/k8-fastbuild/bin/same_names/y'
'-lstatic=exc'
'-lstatic=exc'
...
```
with something like:
```
...
'-Clink-arg=bazel-out/k8-fastbuild/bin/same_names/x/libexc.a'
'-Clink-arg=bazel-out/k8-fastbuild/bin/same_names/y/libexc.a'
...
```
Contributor guide
Assessment
This issue has not been assessed yet.