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

Fix linkopts set in cc deps of bindgen #2422

Merged
merged 13 commits into from
Feb 16, 2024

Conversation

daivinhtran
Copy link
Contributor

@daivinhtran daivinhtran commented Jan 13, 2024

Problem

As of now (before this PR), we seem to mix link_flags file into using for two purposes.

For

cc_library(
    name = "simple",
    srcs = ["simple.cc"],
    hdrs = ["simple.h"],
    linkopts = ["-framework"],
)
rust_bindgen_library(
    name = "simple_bindgen",
    cc_lib = ":simple",
    header = "simple.h",
)
rust_binary(
    name = "simple_example",
    srcs = ["main.rs"],
    deps = [":simple_bindgen"],
)

rust_bindgen_library produces a link_flags file with

-lstatic=simple
-C
link-args=-framework

-lstatic=simple is consumed by rustc whereas -framework is expected to be consumed by an actual linker (either invoked by rustc or cc_common.link)

The flags are then passed to rustc command to compile libsimple_bindgen.rlib. It does not yield any error because -lstatic=simple is correctly used whereas -framework is no-op (there is no linker being invoked for producing rlib). However, the rustc doesn't pass -framework to the linker that link the rust_binary (which is where the cc linkopts matters).

Another problem is that this approach is not compatible with experimental_use_cc_common_link option which let cc_common.link instead of rustc invoke the linker. See #2413

Solution

We're referring "link" as at least two things (which I think what causes problem here):

  1. https://doc.rust-lang.org/rustc/command-line-arguments.html#-l-link-the-generated-crate-to-a-native-library
  2. https://doc.rust-lang.org/rustc/codegen-options/index.html#linker https://doc.rust-lang.org/rustc/codegen-options/index.html#link-args

As proposed in #2415 (comment), this PR splits <rust_library_bindgen>__bindgen.link_flags produced by rust_bindgen rule into two files:

  1. rustc_flags
-lstatic=simple
  1. linker_flags
-framework

We "sort-of" (but not perfectly) had it correct before #2361 when we link the binary directly with the cc_library (aka both kinds of flags).

But since we want to support the Cargo style of linking

cc_lib -> bindgen -> lib_a  -> bin

instead of

cc_lib -> bindgen -> lib_a  -> bin
    \___________________________^

we can pass -lstatic=simple to the rustc command that builds simple_bindgen (rust_library) and propagate linkopts to simple_example (rust_binary) so that the linker can use it.

cc_library -> rust_bindgen ->    rust_library        -> rust_binary
                                                              
                                    -lstatic=simple
                                                            -Clink-args="-framework"

This is long and sounds like a proposal. I'm open for suggestion @UebelAndre @illicitonion @krasimirgg

Fixes #2413

…d `user_link_flags` in bindgen. (bazelbuild#2407)"

This reverts commit 90e4505.
@daivinhtran daivinhtran changed the title Fix link args in bindgen deps Fix linkopts set in cc deps of bindgen Jan 13, 2024
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.

Maybe it's time we added some starlark unit test for the bindgen rules that transitioned targets to use experimental_use_cc_common_link?

rust/private/rustc.bzl Outdated Show resolved Hide resolved
@@ -75,8 +75,8 @@ BuildInfo = provider(
"compile_data": "Depset[File]: Compile data provided by the build script that was not copied into `out_dir`.",
"dep_env": "Optinal[File]: extra build script environment varibles to be set to direct dependencies.",
"flags": "Optional[File]: file containing additional flags to pass to rustc",
"link_flags": "Optional[File]: file containing flags to pass to the linker",
"link_search_paths": "Optional[File]: file containing search paths to pass to the linker",
"linker_flags": "Optional[File]: file containing flags to pass to the linker",
Copy link
Collaborator

Choose a reason for hiding this comment

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

I might have missed it but can you explain the name change?

rust/private/rustc.bzl Outdated Show resolved Hide resolved
@@ -1011,6 +1011,8 @@ def construct_arguments(
env.update(link_env)
rustc_flags.add(ld, format = "--codegen=linker=%s")
rustc_flags.add_joined("--codegen", link_args, join_with = " ", format_joined = "link-args=%s")
# TODO pass linkopts (now in linker_flags_files) from cc deps to linker
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this not a major part of this change? What's the impact of deferring it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This needs to be done before this PR is merged. I was putting up the PR just to make sure the idea makes sense.

rust/private/rustc.bzl Outdated Show resolved Hide resolved
@UebelAndre
Copy link
Collaborator

Also, what would be the impact of exposing a CcInfo provider with linkopts but not the -lstatic flags? That would bubble the flags up to binaries right? While not directly linking libs into the final binary?

@aaronmondal
Copy link

I believe this fixes an issue we encountered where C++ toolchains in nix environments would randomly depend on the host's glibc in some cases.

When we enabled --incompatible_enable_cc_toolchain_resolution (or used Bazel7 which makes this the default) our builds would mostly pass but during compilation (not linking) of higher-level targets we'd get issues like "Hey your glibc version is incompatible" or some completely random errors like "duplicate main function" and failed macro resolutions.

We found that the breakage came "somewhere" from the bindgen logic in rules_rust. I think it was something where our dynamic libcxx from nixpkgs was wrongly linked against the host's glibc instead of the nixpkgs glibc. This then led to these confusing errors down the line.

cc @adam-singer

@daivinhtran
Copy link
Contributor Author

daivinhtran commented Jan 16, 2024

Exposing a CcInfo provider with linkopts but not the -lstatic flags? That would bubble the flags up to binaries right?

@UebelAndre That's what I'm proposing to do and it makes sense to me (see https://bazel.build/reference/be/c-cpp#cc_library.linkopts). -lstatic shouldn't be included in CcInfo because it's a rustc flag.

While not directly linking libs into the final binary?

What do you mean by "libs" here? Rust or cc or both? If you meant "libs" as cc deps, that would reverse what you did in #2361, correct?

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.

What do you mean by "libs" here?

"libs" being the cc_lib targets (C/C++ libraries passed to bindgen)

@UebelAndre That's what I'm proposing to do and it makes sense to me (see https://bazel.build/reference/be/c-cpp#cc_library.linkopts). -lstatic shouldn't be included in CcInfo because it's a rustc flag.

Sweet! But that's not in this PR is it? It looks like there's new plumbing for an additional BuildInfo flags file but that might be unnecessary if we can instead add back CcInfo with a surgical set of flags to get the same results. That said though, maybe it's beneficial to have an extra flags file but right now I question the necessity.

@daivinhtran
Copy link
Contributor Author

It looks like there's new plumbing for an additional BuildInfo flags file but that might be unnecessary if we can instead add back CcInfo with a surgical set of flags to get the same results

This is now done in

CcInfo(
linking_context = cc_common.create_linking_context(
linker_inputs = depset([cc_common.create_linker_input(
owner = ctx.label,
user_link_flags = _get_user_link_flags(cc_lib),
)]),
),
),

That said though, maybe it's beneficial to have an extra flags file but right now I question the necessity.

That's correct. I just removed the linker_flags file.

@UebelAndre PTAL

cc: @thb-sb

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.

Yeah, this is exactly what I was wondering if we could do! If this works for you then I'm happy with the change 😄

@daivinhtran daivinhtran merged commit 8fd0904 into bazelbuild:main Feb 16, 2024
3 checks passed
@daivinhtran daivinhtran deleted the fix-link-args-in-bindgen-deps branch February 16, 2024 04:04
github-merge-queue bot pushed a commit that referenced this pull request Feb 19, 2024
This is a follow-up PR of
#2422.

I'm scoping it to a separate PR to introduce
[@rules_testing](https://github.com/bazelbuild/rules_testing) (a new
Starlark testing framework). The framework removes a lot of boilerplate
code (unncessarily) required when writing analysis tests.
qtica added a commit to qtica/rules_rust that referenced this pull request Apr 1, 2024
This is a follow-up PR of
bazelbuild#2422.

I'm scoping it to a separate PR to introduce
[@rules_testing](https://github.com/bazelbuild/rules_testing) (a new
Starlark testing framework). The framework removes a lot of boilerplate
code (unncessarily) required when writing analysis tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Incorrect linker flags when using cc_common.link
3 participants