Skip to content

Conversation

@tidefield
Copy link
Contributor

@tidefield tidefield 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.
@tidefield tidefield changed the title Fix link args in bindgen deps Fix linkopts set in cc deps of bindgen Jan 13, 2024
@tidefield tidefield force-pushed the fix-link-args-in-bindgen-deps branch from 810d128 to ca9a40f Compare January 13, 2024 00:51
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?

"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?

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.

@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

@tidefield
Copy link
Contributor Author

tidefield 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.

@tidefield
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

@tidefield tidefield requested a review from UebelAndre January 18, 2024 20:10
@tidefield tidefield force-pushed the fix-link-args-in-bindgen-deps branch from 079e06c to db9dcd1 Compare January 18, 2024 21:02
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 😄

@tidefield tidefield merged commit 8fd0904 into bazelbuild:main Feb 16, 2024
@tidefield tidefield 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